/* * 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.SQLException; import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.LockTimeoutException; import javax.persistence.PessimisticLockException; import javax.persistence.Query; import javax.persistence.QueryTimeoutException; import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.jdbc.sql.SolidDBDictionary; import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; import org.apache.openjpa.persistence.exception.PObject; import org.apache.openjpa.persistence.test.SingleEMFTestCase; import org.apache.openjpa.util.OpenJPAException; /** * Tests that correct timeout exceptions are being thrown depending on whether it is a query or a lock operation. * * @author Pinaki Poddar * */ public class TestTimeoutException extends SingleEMFTestCase { private final Class<?> entityClass = PObject.class; public void setUp() { // Disable tests for any DB that has supportsSelectForUpdate==false, like HSQLDictionary OpenJPAEntityManagerFactorySPI tempEMF = emf; if (tempEMF == null) { tempEMF = createEMF(); } assertNotNull(tempEMF); DBDictionary dict = ((JDBCConfiguration)tempEMF.getConfiguration()).getDBDictionaryInstance(); assertNotNull(dict); if (!dict.supportsSelectForUpdate || !dict.supportsQueryTimeout || dict instanceof SolidDBDictionary) setTestsDisabled(true); if (emf == null) { closeEMF(tempEMF); } if (isTestsDisabled()) return; super.setUp(entityClass, CLEAR_TABLES); } public void testQueryTimeOutExceptionWhileQueryingWithLocksOnAlreadyLockedEntities() { if (getLog().isTraceEnabled()) getLog().trace("***** Entered TestTimeoutException." + "testQueryTimeOutExceptionWhileQueryingWithLocksOnAlreadyLockedEntities()"); EntityManager em1 = emf.createEntityManager(); EntityManager em2 = emf.createEntityManager(); assertNotSame(em1, em2); Object oid = createEntity(em1); em1.getTransaction().begin(); Object entity = em1.find(entityClass, oid); assertNotNull(entity); em1.lock(entity, LockModeType.PESSIMISTIC_WRITE); em2.getTransaction().begin(); final Query query = em2.createQuery("select p from PObject p"); query.setLockMode(LockModeType.PESSIMISTIC_WRITE); long timeout = 1000; query.setHint("javax.persistence.query.timeout", timeout); try { query.getResultList(); fail("Expected " + QueryTimeoutException.class.getName()); } catch (Throwable t) { assertError(t, QueryTimeoutException.class); assertTrue(em2.getTransaction().isActive()); } finally { em1.getTransaction().rollback(); em1.close(); em2.getTransaction().rollback(); em2.close(); } } public void testLockTimeOutExceptionWhileLockingAlreadyLockedEntities() { if (getLog().isTraceEnabled()) getLog().trace("***** Entered TestTimeoutException." + "testLockTimeOutExceptionWhileLockingAlreadyLockedEntities()"); EntityManager em1 = emf.createEntityManager(); final EntityManager em2 = emf.createEntityManager(); assertNotSame(em1, em2); final Object oid = createEntity(em1); em1.getTransaction().begin(); final Object entity1 = em1.find(entityClass, oid); assertNotNull(entity1); em1.lock(entity1, LockModeType.PESSIMISTIC_WRITE); em2.getTransaction().begin(); final Object entity2 = em2.find(entityClass, oid); final long timeout = 1000; try { Map<String,Object> hint = new HashMap<String, Object>(); hint.put("javax.persistence.lock.timeout", timeout); em2.lock(entity2, LockModeType.PESSIMISTIC_WRITE, hint); fail("Expected " + PessimisticLockException.class.getName()); } catch (Throwable t) { assertError(t, PessimisticLockException.class); assertTrue(em2.getTransaction().isActive()); } finally { em1.getTransaction().rollback(); em1.close(); em2.getTransaction().rollback(); em2.close(); } } public void testQueryTimeOutExceptionWhileFindWithLocksOnAlreadyLockedEntities() { final int timeout = 1000; if (getLog().isTraceEnabled()) getLog().trace("***** Entered TestTimeoutException." + "testQueryTimeOutExceptionWhileFindWithLocksOnAlreadyLockedEntities()"); EntityManager em1 = emf.createEntityManager(); EntityManager em2 = emf.createEntityManager(); assertNotSame(em1, em2); Object oid = createEntity(em1); em1.getTransaction().begin(); Object entity = em1.find(entityClass, oid); assertNotNull(entity); em1.lock(entity, LockModeType.PESSIMISTIC_WRITE); em2.getTransaction().begin(); try { Map<String,Object> hint = new HashMap<String, Object>(); hint.put("javax.persistence.lock.timeout", timeout); //em2.setProperty("javax.persistence.lock.timeout", timeout); em2.find(entityClass, oid, LockModeType.PESSIMISTIC_WRITE, hint); fail("Expected " + LockTimeoutException.class.getName()); } catch (Throwable t) { assertError(t, LockTimeoutException.class); assertTrue(em2.getTransaction().isActive()); } finally { em1.getTransaction().rollback(); em1.close(); em2.getTransaction().rollback(); em2.close(); } } public Object createEntity(EntityManager em) { long id = System.nanoTime(); em.getTransaction().begin(); PObject pc = new PObject(); pc.setId(id); em.persist(pc); em.getTransaction().commit(); return id; } /** * Assert that an exception of proper type has been thrown. * Also checks that that the exception has populated the failed object. * @param actual exception being thrown * @param expeceted type of the exception */ void assertError(Throwable actual, Class<? extends Throwable> expected) { if (!expected.isAssignableFrom(actual.getClass())) { getLog().error("TestTimeoutException.assertError() - unexpected exception type", actual); //actual.printStackTrace(); print(actual, 0); fail(actual.getClass().getName() + " was raised but expected " + expected.getName()); } Object failed = getFailedObject(actual); assertNotNull("Failed object is null", failed); assertNotEquals("null", failed); } Object getFailedObject(Throwable e) { if (e == null) { getLog().error("TestTimeoutException.getFailedObject() - Object e was null"); return null; } else if (e instanceof LockTimeoutException) { return ((LockTimeoutException) e).getObject(); } else if (e instanceof PessimisticLockException) { return ((PessimisticLockException) e).getEntity(); } else if (e instanceof QueryTimeoutException) { return ((QueryTimeoutException) e).getQuery(); } else if (e instanceof OpenJPAException) { return ((OpenJPAException) e).getFailedObject(); } else { getLog().error("TestTimeoutException.getFailedObject() - unexpected exception type", e); return null; } } void print(Throwable t, int tab) { if (t == null) return; StringBuilder str = new StringBuilder(80); for (int i=0; i<tab*4;i++) str.append(" "); String sqlState = (t instanceof SQLException) ? "(SQLState=" + ((SQLException)t).getSQLState() + ":" + t.getMessage() + ")" : ""; str.append(t.getClass().getName() + sqlState); getLog().error(str); if (t.getCause() == t) return; print(t.getCause(), tab+1); } }