You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by jr...@apache.org on 2009/03/17 05:40:24 UTC

svn commit: r755115 - in /openjpa/trunk/openjpa-persistence-jdbc/src: main/java/org/apache/openjpa/persistence/jdbc/ test/java/org/apache/openjpa/conf/ test/java/org/apache/openjpa/persistence/conf/ test/java/org/apache/openjpa/persistence/query/ test/...

Author: jrbauer
Date: Tue Mar 17 04:40:23 2009
New Revision: 755115

URL: http://svn.apache.org/viewvc?rev=755115&view=rev
Log:
OPENJPA-878 Committing code, tests, and documentation updates for Donald Woods.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryTimeout.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/common/apps/QTimeout.java   (with props)
Modified:
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlan.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestOpenJPAConfiguration.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/query/common/apps/META-INF/persistence.xml

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlan.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlan.java?rev=755115&r1=755114&r2=755115&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlan.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlan.java Tue Mar 17 04:40:23 2009
@@ -145,6 +145,7 @@
     public JDBCFetchPlan setMaxFetchDepth(int depth);
     public JDBCFetchPlan setReadLockMode(LockModeType mode);
     public JDBCFetchPlan setWriteLockMode(LockModeType mode);
+    public JDBCFetchPlan setQueryTimeout(int timeout);
 
     /**
      * @deprecated use the {@link FetchMode} enum instead.

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java?rev=755115&r1=755114&r2=755115&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java Tue Mar 17 04:40:23 2009
@@ -287,4 +287,9 @@
     public JDBCFetchPlan setWriteLockMode(LockModeType mode) {
         return (JDBCFetchPlan) super.setWriteLockMode(mode);
     }
+
+    @Override
+    public JDBCFetchPlan setQueryTimeout(int timeout) {
+        return (JDBCFetchPlan) super.setQueryTimeout(timeout);
+    }
 }

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java?rev=755115&r1=755114&r2=755115&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java Tue Mar 17 04:40:23 2009
@@ -137,9 +137,10 @@
     }
     
     public void testJPAHintSetsFetchPlan() {
-        String jpaKey = "javax.persistence.query.timeout";
-        query.setHint(jpaKey, 5671);
-        assertEquals(5671, query.getFetchPlan().getQueryTimeout());
+        query.setHint("javax.persistence.lock.timeout", 5671);
+        query.setHint("javax.persistence.query.timeout", 7500);
+        assertEquals(5671, query.getFetchPlan().getLockTimeout());
+        assertEquals(7500, query.getFetchPlan().getQueryTimeout());
     }
     
     public void testParts() {

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestOpenJPAConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestOpenJPAConfiguration.java?rev=755115&r1=755114&r2=755115&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestOpenJPAConfiguration.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestOpenJPAConfiguration.java Tue Mar 17 04:40:23 2009
@@ -59,6 +59,7 @@
         map.put("openjpa.ConnectionFactory2", cfactory2);
         map.put("openjpa.Optimistic", Boolean.FALSE);
         map.put("openjpa.LockTimeout", new Integer(503));
+        map.put("javax.persistence.query.timeout", new Integer(1500));
 
         // use new conf so no unexpected restrictions on type of connection
         // factory
@@ -68,6 +69,7 @@
         assertEquals(cfactory2, conf.getConnectionFactory2());
         assertEquals(false, conf.getOptimistic());
         assertEquals(503, conf.getLockTimeout());
+        assertEquals(1500, conf.getQueryTimeout());
 
         OpenJPAConfiguration conf2 = new OpenJPAConfigurationImpl(true, false);
         conf2.fromProperties(map);

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryTimeout.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryTimeout.java?rev=755115&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryTimeout.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryTimeout.java Tue Mar 17 04:40:23 2009
@@ -0,0 +1,601 @@
+/*
+ * 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.openjpa.persistence.query;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.DerbyDictionary;
+import org.apache.openjpa.kernel.Broker;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.PersistenceException;
+import org.apache.openjpa.persistence.QueryTimeoutException;
+import org.apache.openjpa.persistence.query.common.apps.QTimeout;
+import org.apache.openjpa.persistence.test.AllowFailure;
+import org.apache.openjpa.persistence.test.SQLListenerTestCase;
+
+/**
+ * Tests the new query timeout hint support in the JPA 2.0 spec.
+ * 
+ * @version $Rev$ $Date$
+ */
+public class TestQueryTimeout extends SQLListenerTestCase {
+
+    private boolean skipTests = false;
+    // does the DB platform allow retry instead of forcing transaction rollback
+    private boolean supportsQueryTimeoutException = false; 
+
+    @Override
+    public void setUp() {
+        super.setUp(DROP_TABLES, QTimeout.class);
+        getLog().trace("setUp()");
+        String[] _strings = new String[] { "a", "b", "c" };
+        QTimeout qt = null;
+        EntityManager em = null;
+
+        // determine if we should run our tests on this DB platform and what 
+        // exception type to catch
+        DBDictionary dict = ((JDBCConfiguration) emf.getConfiguration())
+            .getDBDictionaryInstance();
+        if ((dict.supportsQueryTimeout) && (dict instanceof DerbyDictionary)) {
+            // set whether we expect to see QueryTimeoutException or 
+            // PersistenceException
+            supportsQueryTimeoutException = false;
+        } else {
+            // FIXME drwoods - OPENJPA-964 - haven't determined what the other 
+            // DBs support
+            // setQueryTimeout is not working with DB2 v9.5.3a on Windows...
+            getLog().info("TestQueryTimeout tests are being skipped, due to " +
+                "DB not supporting Query Timeouts.");
+            skipTests = true;
+            return;
+        }
+
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            getLog().trace("setUp() - creating Qtimeout entities");
+            em.getTransaction().begin();
+            for (int i = 0; i < _strings.length; i++) {
+                qt = new QTimeout(i, _strings[i]);
+                em.persist(qt);
+            }
+            em.getTransaction().commit();
+        } catch (Exception e) {
+            fail("Unexpected setup exception occurred - " + e);
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+
+        // execute some native SQL with no timeouts
+        if (dict instanceof DerbyDictionary) {
+            getLog().trace("setUp() - creating DELAY function only for Derby." +
+                "  Other DBs require manual setup.");
+            // remove existing function if it exists and ignore any errors
+            exec(true, 0, "DROP FUNCTION DELAY");
+            exec(false, 0, "CREATE FUNCTION DELAY(SECONDS INTEGER, " + 
+                "VALUE INTEGER) RETURNS INTEGER PARAMETER STYLE JAVA NO SQL " +
+                "LANGUAGE JAVA EXTERNAL NAME 'org.apache.openjpa.persistence." +
+                "query.TestQueryTimeout.delay'");
+        }
+        getLog().trace("setUp() - creating BEFORE UPDATE/INSERT TRIGGERs for " +
+            "all DBs");
+        exec(false, 0, "CREATE TRIGGER t1 NO CASCADE BEFORE UPDATE ON " +
+            "qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-1)");
+        exec(false, 0, "CREATE TRIGGER t2 NO CASCADE BEFORE INSERT ON " +
+            "qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-2)");
+        // Don't include a DELETE trigger, as it slows down the DROP_TABLES 
+        // cleanup between tests
+        // exec(0, "CREATE TRIGGER t3 NO CASCADE BEFORE DELETE ON qtimeout " +
+        //     "FOR EACH ROW MODE DB2SQL values DELAY(2,-3)");
+    }
+
+    /*
+     * Query timeout scenarios to test for:
+     *   1) By default, there is no timeout
+     *   2) Setting timeout to 0 is same as no timeout (JDBC defined)
+     *     2.1) using the QueryHint annotation
+     *     2.2) calling setHint()
+     *   3) Setting timeout to msecs < DELAY value causes new 
+     *      javax.persistence.QueryTimeoutException when set by:
+     *     3.1) using the QueryHint annotation
+     *     3.2) calling setHint()
+     * Operations to validate through cross coverage of items #1-#3:
+     *   a) getResultList()
+     *   b) getSingleResult()
+     *   c) executeUpdate()
+     * Other behaviors to test for:
+     *   4) Setting timeout to < 0 should be treated as no timeout supplied
+     * Exception generation to test for:
+     *   If the DB query timeout does not cause a transaction rollback, then a 
+     *   QueryTimeoutException should be thrown.
+     *     Applicable to:  unknown
+     *   Else if the DB query timeout causes a transaction rollback, then a 
+     *   PersistenceException should be thrown instead of a QTE.
+     *     Applicable to:  Derby
+     */
+
+    /**
+     * Scenario being tested: 1a) By default, there is no timeout for queries.
+     * Expected Results: The DELAY function is being called and the query takes
+     * 6000+ msecs to complete.
+     */
+    @AllowFailure(value=true, message="FIXME - The runtime >= 6K assertion " +
+            "in this test fails intermittently and needs to be corrected.")
+    public void testQueryTimeout1a() {
+        if (skipTests) {
+            return;
+        }
+        getLog().trace("testQueryTimeout1a() - No Query timeout");
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            Query q = em.createNamedQuery("NoHintList");
+            // verify no default javax.persistence.query.timeout is supplied
+            Map<String, Object> hints = q.getHints();
+            assertFalse(hints.containsKey("javax.persistence.query.timeout"));
+            try {
+                long startTime = System.currentTimeMillis();
+                @SuppressWarnings("unchecked")
+                List results = q.getResultList();
+                long endTime = System.currentTimeMillis();
+                long runTime = endTime - startTime;
+                getLog().trace("testQueryTimeout1a() - NoHintList runTime" + 
+                    " msecs=" + runTime);
+                assertTrue("Should have taken 6000+ msecs", runTime >= 6000);
+                assertEquals("Verify we found the expected number of results.",
+                    2, results.size());
+            } catch (Exception e) {
+                fail("Unexpected testQueryTimeout1a() exception = " + e);
+            }
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Scenario being tested: 1c) By default, there is no timeout for updates.
+     * Expected Results: The DELAY function is being called and the query takes
+     * 2000+ msecs to complete.
+     */
+    public void testQueryTimeout1c() {
+        if (skipTests) {
+            return;
+        }
+        getLog().trace("testQueryTimeout1c() - No Update timeout");
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            try {
+                long startTime = System.currentTimeMillis();
+                QTimeout qt = em.find(QTimeout.class, new Integer(1));
+                em.getTransaction().begin();
+                qt.setStringField("updated");
+                em.flush();
+                em.getTransaction().commit();
+                long endTime = System.currentTimeMillis();
+                long runTime = endTime - startTime;
+                getLog().trace("testQueryTimeout1c() - EM find/update runTime" +
+                    " msecs=" + runTime);
+                assertTrue("Should have taken 2000+ msecs", runTime >= 2000);
+                em.clear();
+                qt = em.find(QTimeout.class, new Integer(1));
+                assertEquals("Verify the entity was updated.",
+                    qt.getStringField(), "updated");
+            } catch (Exception e) {
+                fail("Unexpected testQueryTimeout1c() exception = " + e);
+            }
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Scenario being tested: 2.1.a) Explicit annotated QueryHint of timeout=0
+     * is treated the same as the default no timeout for queries.
+     * Expected Results: The DELAY function is being called and the query
+     * takes 6000+ msecs to complete.
+     */
+    @AllowFailure(value=true, message="FIXME - The runtime >= 6K assertion " +
+        "in this test fails intermittently and needs to be corrected.")
+    public void testQueryTimeout2a() {
+        if (skipTests) {
+            return;
+        }
+        getLog().trace("testQueryTimeout2a() - QueryHint=0");
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            Query q = em.createNamedQuery("Hint0msec");
+            // verify javax.persistence.query.timeout is supplied
+            Map<String, Object> hints = q.getHints();
+            assertTrue(hints.containsKey("javax.persistence.query.timeout"));
+            Integer timeout = new Integer(
+                (String) hints.get("javax.persistence.query.timeout"));
+            getLog().trace("testQueryTimeout2a() - Retrieved hint " + 
+                "javax.persistence.query.timeout=" + timeout);
+            assertEquals(timeout, new Integer(0));
+
+            try {
+                long startTime = System.currentTimeMillis();
+                @SuppressWarnings("unchecked")
+                List results = q.getResultList();
+                long endTime = System.currentTimeMillis();
+                long runTime = endTime - startTime;
+                getLog().trace("testQueryTimeout2a() - Hint0msec runTime msecs="
+                    + runTime);
+                assertTrue("Should have taken 6000+ msecs", runTime >= 6000);
+                assertEquals("Verify we found the expected number of results.",
+                    2, results.size());
+            } catch (Exception e) {
+                fail("Unexpected testQueryTimeout2a() exception = " + e);
+            }
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Scenario being tested: 2.1.b) Explicit setHint of timeout=0 is treated
+     * the same as the default no timeout for queries.
+     * Expected Results: The DELAY function is being called and the query
+     * takes 2000+ msecs to complete.
+     */
+    public void testQueryTimeout2b() {
+        if (skipTests) {
+            return;
+        }
+        Integer setTime = new Integer(0);
+        getLog().trace("testQueryTimeout2b() - setHint(" + setTime + ")");
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            Query q = em.createNamedQuery("NoHintSingle");
+
+            // verify no default javax.persistence.query.timeout is supplied
+            Map<String, Object> hints = q.getHints();
+            assertFalse(hints.containsKey("javax.persistence.query.timeout"));
+
+            // update the timeout value to 0 and verify it was set
+            getLog().trace("testQueryTimeout2b() - Setting hint " + 
+                "javax.persistence.query.timeout=" + setTime);
+            q.setHint("javax.persistence.query.timeout", setTime);
+            hints = q.getHints();
+            assertTrue(hints.containsKey("javax.persistence.query.timeout"));
+            Integer timeout = (Integer) hints.get(
+                "javax.persistence.query.timeout");
+            getLog().trace("testQueryTimeout2b() - Retrieved hint " +
+                "javax.persistence.query.timeout=" + timeout);
+            assertEquals(timeout, setTime);
+
+            try {
+                long startTime = System.currentTimeMillis();
+                Object result = q.getSingleResult();
+                long endTime = System.currentTimeMillis();
+                long runTime = endTime - startTime;
+                getLog().trace("testQueryTimeout2b() - NoHintSingle runTime " + 
+                    "msecs=" + runTime);
+                assertTrue("Should have taken 2000+ msecs", runTime >= 2000);
+                assertNotNull("Verify we received a result.", result);
+            } catch (Exception e) {
+                fail("Unexpected testQueryTimeout2b() exception = " + e);
+            }
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Scenario being tested: 3.1.a) Explicit annotated QueryHint of
+     * timeout=1000 msecs will cause the query to timeout.
+     * Expected Results: QueryTimeoutException or PersistenceException
+     */
+    public void testQueryTimeout3a() {
+        if (skipTests) {
+            return;
+        }
+        Integer setTime = new Integer(1000);
+        getLog().trace("testQueryTimeout3a() - QueryHint(" + setTime + ")");
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            Query q = em.createNamedQuery("Hint1000msec");
+
+            // verify javax.persistence.query.timeout hint via annotation set
+            Map<String, Object> hints = q.getHints();
+            assertTrue(hints.containsKey("javax.persistence.query.timeout"));
+            Integer timeout = new Integer((String) hints.get(
+                "javax.persistence.query.timeout"));
+            getLog().trace(
+                "testQueryTimeout3a() - Found javax.persistence.query.timeout="
+                + timeout);
+            assertTrue("Expected to find a javax.persistence.query.timeout="
+                + setTime, (timeout.intValue() == setTime.intValue()));
+
+            try {
+                long startTime = System.currentTimeMillis();
+                @SuppressWarnings( { "unchecked", "unused" })
+                List results = q.getResultList();
+                long endTime = System.currentTimeMillis();
+                long runTime = endTime - startTime;
+                getLog().trace(
+                    "testQueryTimeout3a() - Hint1000msec runTime msecs="
+                    + runTime);
+                //assertEquals("Should never get valid results due to the " + 
+                // "timeout.", 2, results.size());
+                fail("QueryTimeout annotation failed to cause an Exception " +
+                    "in testQueryTimeout3a("
+                    + setTime + " msecs), runTime msecs=" + runTime);
+            } catch (Exception e) {
+                // expected
+                checkException("testQueryTimeout3a()", e);
+            }
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Scenario being tested: 3.2.b) Explicit setHint of timeout to 1000 msecs
+     * will cause the query to timeout.
+     * Expected Results: QueryTimeoutException or PersistenceException
+     */
+    public void testQueryTimeout3b() {
+        if (skipTests) {
+            return;
+        }
+        Integer setTime = new Integer(1000);
+        getLog().trace("testQueryTimeout3b() - setHint(" + setTime + ")");
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            Query q = em.createNamedQuery("NoHintSingle");
+
+            // verify no default javax.persistence.query.timeout is supplied
+            Map<String, Object> hints = q.getHints();
+            assertFalse(hints.containsKey("javax.persistence.query.timeout"));
+
+            // update the timeout value and verify it was set
+            getLog().trace("testQueryTimeout3b() - Setting hint " +
+                "javax.persistence.query.timeout=" + setTime);
+            q.setHint("javax.persistence.query.timeout", setTime);
+            hints = q.getHints();
+            assertTrue(hints.containsKey("javax.persistence.query.timeout"));
+            Integer timeout = (Integer) hints.get(
+                "javax.persistence.query.timeout");
+            assertEquals(timeout, setTime);
+
+            try {
+                long startTime = System.currentTimeMillis();
+                @SuppressWarnings("unused")
+                Object result = q.getSingleResult();
+                long endTime = System.currentTimeMillis();
+                long runTime = endTime - startTime;
+                getLog().trace(
+                    "testQueryTimeout3b() - NoHintSingle runTime msecs="
+                    + runTime);
+                //assertNull("Should never get valid result due to the timeout.", result);
+                fail("QueryTimeout annotation failed to cause an Exception " + 
+                    "in testQueryTimeout3b("
+                    + setTime + " mscs), runTime msecs=" + runTime);
+            } catch (Exception e) {
+                // expected
+                checkException("testQueryTimeout3b()", e);
+            }
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Scenario being tested: 4) Timeouts < 0 are ignored and treated as the
+     * default no timeout scenario.
+     * Expected Results: The DELAY function is being called and the query
+     * takes 2000+ msecs to complete.
+     */
+    public void testQueryTimeout4() {
+        if (skipTests) {
+            return;
+        }
+        Integer setTime = new Integer(-2000);
+        getLog().trace("testQueryTimeout4() - setHint(" + setTime + ")");
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            Query q = em.createNamedQuery("NoHintSingle");
+
+            // verify no default javax.persistence.query.timeout is supplied
+            Map<String, Object> hints = q.getHints();
+            assertFalse(hints.containsKey("javax.persistence.query.timeout"));
+
+            // update the timeout value to -2000 and verify it was set
+            getLog().trace("testQueryTimeout4() - Setting hint " +
+                "javax.persistence.query.timeout="
+                + setTime);
+            q.setHint("javax.persistence.query.timeout", setTime);
+            hints = q.getHints();
+            assertTrue(hints.containsKey("javax.persistence.query.timeout"));
+            Integer timeout = (Integer) hints.get(
+                "javax.persistence.query.timeout");
+            getLog().trace("testQueryTimeout4() - Retrieved hint " +
+                "javax.persistence.query.timeout="
+                + timeout);
+            assertEquals(timeout, setTime);
+
+            try {
+                long startTime = System.currentTimeMillis();
+                Object result = q.getSingleResult();
+                long endTime = System.currentTimeMillis();
+                long runTime = endTime - startTime;
+                getLog().trace(
+                    "testQueryTimeout4() - NoHintSingle runTime msecs="
+                    + runTime);
+                assertTrue("Should have taken 2000+ msecs", runTime >= 2000);
+                assertNotNull("Verify we received a result.", result);
+            } catch (Exception e) {
+                fail("Unexpected testQueryTimeout4() exception = " + e);
+            }
+        } finally {
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Internal convenience method to execute SQL statements
+     * 
+     * @param em
+     * @param sql
+     * @param timeoutSecs
+     * @param fail
+     */
+    private void exec(boolean ignoreExceptions, int timeoutSecs, String sql) {
+        EntityManager em = null;
+        Statement s = null;
+        try {
+            em = emf.createEntityManager();
+            assertNotNull(em);
+            Broker broker = JPAFacadeHelper.toBroker(em);
+            Connection conn = (Connection) broker.getConnection();
+            s = conn.createStatement();
+            if (timeoutSecs > 0) {
+                s.setQueryTimeout(timeoutSecs);
+            }
+            getLog().trace("execute(" + sql + ")");
+            s.execute(sql);
+        } catch (SQLException sqe) {
+            if (!ignoreExceptions) {
+                fail(sqe.toString());
+            }
+        } finally {
+            if (s != null) {
+                try {
+                    s.close();
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+            if ((em != null) && em.isOpen()) {
+                em.close();
+            }
+        }
+    }
+
+    /**
+     * Internal convenience method for getting the OpenJPA logger
+     * 
+     * @return
+     */
+    private Log getLog() {
+        return emf.getConfiguration().getLog("Tests");
+    }
+
+    /**
+     * Internal convenience method for checking that the given Exception matches
+     * the expected type for a given DB platform.
+     * 
+     * @param test
+     * @param e
+     */
+    private void checkException(String test, Exception e) {
+        if (supportsQueryTimeoutException) {
+            assertTrue("Expected QueryTimeoutException instead of " + e,
+                matchesExpectedException(QueryTimeoutException.class, e));
+        } else {
+            assertTrue("Expected PersistenceException instead of " + e,
+                matchesExpectedException(PersistenceException.class, e));
+        }
+        getLog().trace(test + " - Caught expected Exception = " + e);
+    }
+
+    /**
+     * Internal convenience method for checking that the given Exception matches
+     * the expected type.
+     * 
+     * @param expected
+     * @param tested
+     * @return
+     */
+    private boolean matchesExpectedException(Class<?> expected, 
+        Exception tested) {
+        assertNotNull(expected);
+        boolean exMatched = false;
+        if (tested != null) {
+            Class<?> testExClass = tested.getClass();
+            exMatched = expected.isAssignableFrom(testExClass);
+        }
+        return exMatched;
+    }
+
+    /**
+     * This is the user-defined DB FUNCTION which is called from our queries to
+     * sleep and cause timeouts, based on seconds.
+     * 
+     * @param secs
+     * @param value
+     * @return value
+     * @throws SQLException
+     */
+    public static int delay(int secs, int value) throws SQLException {
+        try {
+            /*
+            if (value >= 0) {
+                System.out.println("  Native SQL called delay(secs=" + secs + 
+                ",value=" + value + ")");
+            } else {
+                System.out.println("   Trigger called delay(secs=" + secs + 
+                ",value=" + value + ")");
+            }
+            */
+            Thread.sleep(secs * 1000);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+        return value;
+    }
+
+    public static void main(String[] args) {
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryTimeout.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/common/apps/QTimeout.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/common/apps/QTimeout.java?rev=755115&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/common/apps/QTimeout.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/common/apps/QTimeout.java Tue Mar 17 04:40:23 2009
@@ -0,0 +1,86 @@
+/*
+ * 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.openjpa.persistence.query.common.apps;
+
+import java.io.Serializable;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedNativeQueries;
+import javax.persistence.NamedNativeQuery;
+import javax.persistence.QueryHint;
+import javax.persistence.Table;
+import javax.persistence.Version;
+
+@Entity
+@Table(name = "qtimeout")
+@NamedNativeQueries({
+@NamedNativeQuery(name = "NoHintList", 
+    query = "select id from qtimeout where mod(DELAY(2,id),2)=0"),
+@NamedNativeQuery(name = "NoHintSingle", 
+    query = "select id from qtimeout where mod(DELAY(2,id),2)=1"),
+@NamedNativeQuery(name = "Hint0msec", 
+    query = "select id from qtimeout where mod(DELAY(2,id),2)=0", 
+    hints = { @QueryHint(name = "javax.persistence.query.timeout", 
+        value = "0") }),
+@NamedNativeQuery(name = "Hint1000msec", 
+    query = "select id from qtimeout where mod(DELAY(2,id),2)=0", 
+    hints = { @QueryHint(name = "javax.persistence.query.timeout", 
+        value = "1000") })
+})
+public class QTimeout implements Serializable {
+    private static final long serialVersionUID = -622382368446668547L;
+
+    @Id
+    protected int id;
+
+    @Basic
+    @Column(length = 35)
+    protected String stringField;
+
+    @Version
+    protected int versionField;
+
+    public QTimeout() {
+    }
+
+    public QTimeout(int i, String s) {
+        this.id = i;
+        this.stringField = s;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setStringField(String val) {
+        stringField = val;
+    }
+
+    public String getStringField() {
+        return stringField;
+    }
+
+    @Override
+    public String toString() {
+        return ("id: " + id + " StringField: " + stringField);
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/common/apps/QTimeout.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/query/common/apps/META-INF/persistence.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/query/common/apps/META-INF/persistence.xml?rev=755115&r1=755114&r2=755115&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/query/common/apps/META-INF/persistence.xml (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/query/common/apps/META-INF/persistence.xml Tue Mar 17 04:40:23 2009
@@ -45,5 +45,6 @@
 		<class>org.apache.openjpa.persistence.query.common.apps.CircularFKPC2</class>
 		<class>org.apache.openjpa.persistence.query.common.apps.ModRuntimeTest1</class>
 		<class>org.apache.openjpa.persistence.query.common.apps.ModRuntimeTest2</class>
+        <class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class>
 	</persistence-unit>
 </persistence>