You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by mt...@apache.org on 2009/12/01 22:13:31 UTC

svn commit: r885935 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/ openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/

Author: mtylenda
Date: Tue Dec  1 21:13:30 2009
New Revision: 885935

URL: http://svn.apache.org/viewvc?rev=885935&view=rev
Log:
OPENJPA-1248: Improve LOB streaming with PostgreSQL when connections are being wrapped by data source; refactor and modify test case not to run Reader LOB tests while on PostgreSQL

Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/AbstractLobTest.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/TestInputStreamLob.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java?rev=885935&r1=885934&r2=885935&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java Tue Dec  1 21:13:30 2009
@@ -24,6 +24,7 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.security.AccessController;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.PreparedStatement;
@@ -47,6 +48,7 @@
 import org.apache.openjpa.lib.jdbc.DelegatingConnection;
 import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
 import org.apache.openjpa.lib.util.ConcreteClassGenerator;
+import org.apache.openjpa.lib.util.J2DoPrivHelper;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.util.InternalException;
@@ -56,7 +58,7 @@
 import org.postgresql.largeobject.LargeObjectManager;
 
 /**
- * Dictionary for Postgres.
+ * Dictionary for PostgreSQL.
  */
 public class PostgresDictionary
     extends DBDictionary {
@@ -67,6 +69,9 @@
     private static Constructor<PostgresConnection> postgresConnectionImpl;
     private static Constructor<PostgresPreparedStatement> postgresPreparedStatementImpl;
 
+    private Method dbcpGetDelegate;
+    private Method connectionUnwrap;
+
     static {
         try {
             postgresConnectionImpl = ConcreteClassGenerator.getConcreteConstructor(
@@ -385,8 +390,7 @@
         DelegatingConnection conn = (DelegatingConnection)store
             .getConnection();
         conn.setAutoCommit(false);
-        LargeObjectManager lom = ((PGConnection)conn.getInnermostDelegate())
-        .getLargeObjectAPI();
+        LargeObjectManager lom = getLargeObjectManager(conn);
         if (rs.getInt(column) != -1) {
             LargeObject lo = lom.open(rs.getInt(column));
             return lo.getInputStream();
@@ -412,8 +416,7 @@
             .getConnection();
             try {
                 conn.setAutoCommit(false);
-                PGConnection pgconn = (PGConnection)conn.getInnermostDelegate();
-                LargeObjectManager lom = pgconn.getLargeObjectAPI();
+                LargeObjectManager lom = getLargeObjectManager(conn);
                 // The create method is valid in versions previous to 8.3
                 // in 8.3 this method is deprecated, use createLO
                 int oid = lom.create();
@@ -451,9 +454,7 @@
             int oid = res.getInt(1);
             if (oid != -1) {
                 conn.setAutoCommit(false);
-                PGConnection pgconn = (PGConnection)conn
-                    .getInnermostDelegate();
-                LargeObjectManager lom = pgconn.getLargeObjectAPI();
+                LargeObjectManager lom = getLargeObjectManager(conn);
                 if (ob != null) {
                     LargeObject lo = lom.open(oid, LargeObjectManager.WRITE);
                     OutputStream os = lo.getOutputStream();
@@ -466,9 +467,7 @@
             } else {
                 if (ob != null) {
                     conn.setAutoCommit(false);
-                    PGConnection pgconn = (PGConnection)conn
-                        .getInnermostDelegate();
-                    LargeObjectManager lom = pgconn.getLargeObjectAPI();
+                    LargeObjectManager lom = getLargeObjectManager(conn);
                     oid = lom.create();
                     LargeObject lo = lom.open(oid, LargeObjectManager.WRITE);
                     OutputStream os = lo.getOutputStream();
@@ -514,9 +513,7 @@
             int oid = res.getInt(1);
             if (oid != -1) {
                 conn.setAutoCommit(false);
-                PGConnection pgconn = (PGConnection)conn
-                    .getInnermostDelegate();
-                LargeObjectManager lom = pgconn.getLargeObjectAPI();
+                LargeObjectManager lom = getLargeObjectManager(conn);
                 lom.delete(oid);
             }
         } finally {
@@ -677,6 +674,75 @@
     }
 
     /**
+     * Get the native PostgreSQL Large Object Manager used for LOB handling.
+     */
+    protected LargeObjectManager getLargeObjectManager(DelegatingConnection conn) throws SQLException {
+        return getPGConnection(conn).getLargeObjectAPI();
+    }
+
+    /**
+     * Get the native PostgreSQL connection from the given connection.
+     * Various attempts of unwrapping are being performed.
+     */
+    protected PGConnection getPGConnection(DelegatingConnection conn) {
+        Connection innerConn = conn.getInnermostDelegate();
+        if (innerConn instanceof PGConnection) {
+            return (PGConnection) innerConn;
+        }
+        if (innerConn.getClass().getName().startsWith("org.apache.commons.dbcp")) {
+            return (PGConnection) getDbcpDelegate(innerConn);
+        }
+        return (PGConnection) unwrapConnection(conn, PGConnection.class);
+    }
+
+    /**
+     * Get the delegated connection from the given DBCP connection.
+     * 
+     * @param conn must be a DBCP connection
+     * @return connection the DBCP connection delegates to
+     */
+    protected Connection getDbcpDelegate(Connection conn) {
+        Connection delegate = null;
+        try {
+            if (dbcpGetDelegate == null) {
+                Class<?> dbcpConnectionClass =
+                    Class.forName("org.apache.commons.dbcp.DelegatingConnection", true, AccessController
+                        .doPrivileged(J2DoPrivHelper.getContextClassLoaderAction()));
+                
+                dbcpGetDelegate = dbcpConnectionClass.getMethod("getInnermostDelegate");
+            }
+            delegate = (Connection) dbcpGetDelegate.invoke(conn);
+        } catch (Exception e) {
+            throw new InternalException(_loc.get("dbcp-unwrap-failed"), e);
+        }
+        if (delegate == null) {
+            throw new InternalException(_loc.get("dbcp-unwrap-failed"));
+        }
+        return delegate;
+    }
+
+    /**
+     * Get (unwrap) the delegated connection from the given connection.
+     * Use reflection to attempt to unwrap a connection.
+     * Note: This is a JDBC 4 operation, so it requires a Java 6 environment 
+     * with a JDBC 4 driver or data source to have any chance of success.
+     * 
+     * @param conn a delegating connection
+     * @param connectionClass the expected type of delegated connection
+     * @return connection the given connection delegates to
+     */
+    private Connection unwrapConnection(Connection conn, Class<?> connectionClass) {
+        try {
+            if (connectionUnwrap == null) {
+                connectionUnwrap = Connection.class.getMethod("unwrap", Class.class);
+            }
+            return (Connection) connectionUnwrap.invoke(conn, connectionClass);
+        } catch (Exception e) {
+            throw new InternalException(_loc.get("connection-unwrap-failed"), e);
+        }
+    }
+
+    /**
      * Connection wrapper to work around the postgres empty result set bug.
      */
     protected abstract static class PostgresConnection

Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties?rev=885935&r1=885934&r2=885935&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties Tue Dec  1 21:13:30 2009
@@ -194,3 +194,9 @@
     continue processing. If this is a benign error you may disable it entirely \
 	by setting the supportsQueryTimeout attribute on the DBDictionary to false.\
 	The exception thrown was {1}.
+dbcp-unwrap-failed: Unable to get underlying connection from DBCP pooled \
+	connection. Make sure the DBCP property AccessToUnderlyingConnectionAllowed \
+	is enabled.
+connection-unwrap-failed: Unable to get underlying connection from pooled \
+	connection. Java version 6 and a version 4 capable JDBC driver \
+	or data source are minimum requirements to perform this operation.

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/AbstractLobTest.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/AbstractLobTest.java?rev=885935&r1=885934&r2=885935&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/AbstractLobTest.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/AbstractLobTest.java Tue Dec  1 21:13:30 2009
@@ -20,17 +20,18 @@
 package org.apache.openjpa.jdbc.meta.strats;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 import javax.persistence.EntityManager;
 import javax.persistence.Query;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.datacache.DataCachePCData;
-import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.sql.DBDictionary;
 import org.apache.openjpa.jdbc.sql.MySQLDictionary;
 import org.apache.openjpa.jdbc.sql.OracleDictionary;
-import org.apache.openjpa.jdbc.sql.PostgresDictionary;
 import org.apache.openjpa.jdbc.sql.SQLServerDictionary;
 import org.apache.openjpa.meta.ClassMetaData;
 import org.apache.openjpa.persistence.JPAFacadeHelper;
@@ -46,25 +47,22 @@
 
 public abstract class AbstractLobTest extends SingleEMFTestCase {
 
+    protected List<Class<? extends DBDictionary>> supportedDatabases =
+        new ArrayList<Class<? extends DBDictionary>>
+            (Arrays.asList(MySQLDictionary.class, OracleDictionary.class, SQLServerDictionary.class));
+        
     public void setUp() throws Exception {
+        setSupportedDatabases(supportedDatabases.toArray(new Class<?>[] {}));
+        if (isTestsDisabled()) {
+            return;
+        }
+
         super.setUp(getLobEntityClass(), CLEAR_TABLES,
             "openjpa.DataCache", "true",
             "openjpa.RemoteCommitProvider", "sjvm",
             "openjpa.ConnectionRetainMode", "transaction");
     }
 
-    public boolean isDatabaseSupported() {
-        DBDictionary dict = ((JDBCConfiguration) emf.getConfiguration())
-            .getDBDictionaryInstance();
-        if (dict instanceof MySQLDictionary ||
-            dict instanceof SQLServerDictionary ||
-            dict instanceof OracleDictionary ||
-            dict instanceof PostgresDictionary) {
-            return true;
-        }
-        return false;
-    }
-
     public void insert(LobEntity le) {
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -74,12 +72,10 @@
     }
 
     public void testInsert() {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity("oOOOOOo", 1));
     }
 
     public void testInsertAndSelect() throws IOException {
-        if (!isDatabaseSupported()) return;
         String s = "oooOOOooo";
         insert(newLobEntity(s, 1));
         EntityManager em = emf.createEntityManager();
@@ -94,7 +90,6 @@
     }
 
     public void testInsertNull() {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity(null, 1));
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -105,7 +100,6 @@
     }
 
     public void testUpdate() throws IOException {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity("oOOOOOo", 1));
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -123,7 +117,6 @@
     }
 
     public void testUpdateWithNull() {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity("oOOOOOo", 1));
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -140,7 +133,6 @@
     }
     
     public void testUpdateANullObjectWithoutNull() throws IOException {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity(null, 1));
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -158,7 +150,6 @@
     }
     
     public void testDelete() {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity("oOOOOOo", 1));
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -175,7 +166,6 @@
     }
     
     public void testLifeCycleInsertFlushModify() {
-        if (!isDatabaseSupported()) return;
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
         LobEntity le = newLobEntity("oOOOOOo", 1);
@@ -187,7 +177,6 @@
     }
 
     public void testLifeCycleLoadFlushModifyFlush() {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity("oOOOOOo", 1));
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -201,7 +190,6 @@
 
     public void testReadingMultipleTimesWithASingleConnection()
         throws IOException {
-        if (!isDatabaseSupported()) return;
         insert(newLobEntity("oOOOOOo", 1));
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
@@ -222,7 +210,6 @@
     }
 
     public void testDataCache() {
-        if (!isDatabaseSupported()) return;
         OpenJPAEntityManager em = emf.createEntityManager();
 
         em.getTransaction().begin();
@@ -241,7 +228,6 @@
     }
 
     public void testSetResetAndFlush() throws IOException {
-        if (!isDatabaseSupported()) return;
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
         LobEntity le = newLobEntity("oOOOOOo", 1);
@@ -259,7 +245,6 @@
     }
 
     public void testSetFlushAndReset() throws IOException {
-        if (!isDatabaseSupported()) return;
         EntityManager em = emf.createEntityManager();
         em.getTransaction().begin();
         LobEntity le = newLobEntity("oOOOOOo", 1);

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/TestInputStreamLob.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/TestInputStreamLob.java?rev=885935&r1=885934&r2=885935&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/TestInputStreamLob.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/meta/strats/TestInputStreamLob.java Tue Dec  1 21:13:30 2009
@@ -22,6 +22,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.openjpa.jdbc.sql.PostgresDictionary;
+
 /**
  * Defines all the abstract methods from AbstractLobTest to tests the
  * the LOB support with an InputStream.
@@ -32,6 +34,12 @@
 
 public class TestInputStreamLob extends AbstractLobTest {
 
+    @Override
+    public void setUp() throws Exception {
+        supportedDatabases.add(PostgresDictionary.class);
+        super.setUp();
+    }
+
     protected LobEntity newLobEntity(String s, int id) {
         InputStreamLobEntity isle = new InputStreamLobEntity();
         isle.setId(id);