Author: smarlow(a)redhat.com
Date: 2010-02-22 18:27:16 -0500 (Mon, 22 Feb 2010)
New Revision: 18852
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java
core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractQueryImpl.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/QueryHints.java
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java
Log:
HHH-4662 Implement javax.persistence.query.timeout
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java
===================================================================
---
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java 2010-02-22
21:59:43 UTC (rev 18851)
+++
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java 2010-02-22
23:27:16 UTC (rev 18852)
@@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
@@ -69,7 +69,7 @@
queryName,
getBoolean( queryName, "org.hibernate.cacheable", hints ),
getString( queryName, "org.hibernate.cacheRegion", hints ),
- getInteger( queryName, "org.hibernate.timeout", hints ),
+ getTimeout( queryName, hints ),
getInteger( queryName, "org.hibernate.fetchSize", hints ),
getFlushMode( queryName, hints ),
getCacheMode( queryName, hints ),
@@ -105,7 +105,7 @@
null,
getBoolean( queryName, "org.hibernate.cacheable", hints ),
getString( queryName, "org.hibernate.cacheRegion", hints ),
- getInteger( queryName, "org.hibernate.timeout", hints ),
+ getTimeout( queryName, hints ),
getInteger( queryName, "org.hibernate.fetchSize", hints ),
getFlushMode( queryName, hints ),
getCacheMode( queryName, hints ),
@@ -126,7 +126,7 @@
null,
getBoolean( queryName, "org.hibernate.cacheable", hints ),
getString( queryName, "org.hibernate.cacheRegion", hints ),
- getInteger( queryName, "org.hibernate.timeout", hints ),
+ getTimeout( queryName, hints ),
getInteger( queryName, "org.hibernate.fetchSize", hints ),
getFlushMode( queryName, hints ),
getCacheMode( queryName, hints ),
@@ -407,4 +407,18 @@
}
return null;
}
+
+ private static Integer getTimeout(String queryName, QueryHint[] hints) {
+ Integer timeout = getInteger( queryName, "javax.persistence.query.timeout",
hints );
+
+ if ( timeout != null ) {
+ // convert milliseconds to seconds
+ timeout = new Integer ((int)Math.round(timeout.doubleValue() / 1000.0 ) );
+ }
+ else {
+ // timeout is already in seconds
+ timeout = getInteger( queryName, "org.hibernate.timeout", hints );
+ }
+ return timeout;
+ }
}
Modified: core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java
===================================================================
---
core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java 2010-02-22
21:59:43 UTC (rev 18851)
+++
core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java 2010-02-22
23:27:16 UTC (rev 18852)
@@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
@@ -26,6 +26,7 @@
import org.hibernate.JDBCException;
import org.hibernate.PessimisticLockException;
+import org.hibernate.QueryTimeoutException;
import java.sql.SQLException;
import java.util.HashSet;
@@ -115,6 +116,13 @@
// Derby "A lock could not be obtained within the time requested."
return new PessimisticLockException( message, sqlException, sql );
}
+
+ // MySQL Query execution was interrupted
+ if ( "70100".equals( sqlState ) ||
+ // Oracle user requested cancel of current operation
+ "72000".equals( sqlState ) ) {
+ throw new QueryTimeoutException( message, sqlException, sql );
+ }
}
return handledNonSpecificException( sqlException, message, sql );
Modified:
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
===================================================================
---
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2010-02-22
21:59:43 UTC (rev 18851)
+++
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2010-02-22
23:27:16 UTC (rev 18852)
@@ -1130,6 +1130,11 @@
handlePersistenceException( converted );
return converted;
}
+ else if ( e instanceof org.hibernate.QueryTimeoutException ) {
+ QueryTimeoutException converted = new QueryTimeoutException(e.getMessage(), e);
+ handlePersistenceException( converted );
+ return converted;
+ }
else if ( e instanceof ObjectNotFoundException ) {
EntityNotFoundException converted = new EntityNotFoundException( e.getMessage() );
handlePersistenceException( converted );
Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractQueryImpl.java
===================================================================
---
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractQueryImpl.java 2010-02-22
21:59:43 UTC (rev 18851)
+++
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractQueryImpl.java 2010-02-22
23:27:16 UTC (rev 18852)
@@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
+ * Copyright (c) 2010 by Red Hat Inc and/or its affiliates or by
* third-party contributors as indicated by either @author tags or express
* copyright attribution statements applied by the authors. All
* third-party contributions are distributed under license by Red Hat Inc.
@@ -50,6 +50,7 @@
import static org.hibernate.ejb.QueryHints.HINT_FLUSH_MODE;
import static org.hibernate.ejb.QueryHints.HINT_READONLY;
import static org.hibernate.ejb.QueryHints.HINT_TIMEOUT;
+import static org.hibernate.ejb.QueryHints.SPEC_HINT_TIMEOUT;
import org.hibernate.ejb.util.CacheModeHelper;
import org.hibernate.ejb.util.ConfigurationHelper;
@@ -204,6 +205,11 @@
if ( HINT_TIMEOUT.equals( hintName ) ) {
applyTimeout( ConfigurationHelper.getInteger( value ) );
}
+ else if ( SPEC_HINT_TIMEOUT.equals( hintName ) ) {
+ // convert milliseconds to seconds
+ int timeout = (int)Math.round(ConfigurationHelper.getInteger( value ).doubleValue() /
1000.0 );
+ applyTimeout( new Integer(timeout) );
+ }
else if ( HINT_COMMENT.equals( hintName ) ) {
applyComment( (String) value );
}
Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/QueryHints.java
===================================================================
--- core/trunk/entitymanager/src/main/java/org/hibernate/ejb/QueryHints.java 2010-02-22
21:59:43 UTC (rev 18851)
+++ core/trunk/entitymanager/src/main/java/org/hibernate/ejb/QueryHints.java 2010-02-22
23:27:16 UTC (rev 18852)
@@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
+ * Copyright (c) 2010 by Red Hat Inc and/or its affiliates or by
* third-party contributors as indicated by either @author tags or express
* copyright attribution statements applied by the authors. All
* third-party contributions are distributed under license by Red Hat Inc.
@@ -32,7 +32,12 @@
* @author Steve Ebersole
*/
public class QueryHints {
- public static final String HINT_TIMEOUT = "org.hibernate.timeout";
+ /**
+ * @deprecated HINT_TIMEOUT (org.hibernate.timeout),
+ * instead use SPEC_HINT_TIMEOUT (javax.persistence.query.timeout)
+ */
+ public static final String HINT_TIMEOUT = "org.hibernate.timeout"; // Query
timeout in seconds
+ public static final String SPEC_HINT_TIMEOUT =
"javax.persistence.query.timeout"; // timeout in milliseconds
public static final String HINT_COMMENT = "org.hibernate.comment";
public static final String HINT_FETCH_SIZE = "org.hibernate.fetchSize";
public static final String HINT_CACHE_REGION = "org.hibernate.cacheRegion";
@@ -46,6 +51,7 @@
private static Set<String> buildHintsSet() {
HashSet<String> hints = new HashSet<String>();
hints.add( HINT_TIMEOUT );
+ hints.add( SPEC_HINT_TIMEOUT );
hints.add( HINT_COMMENT );
hints.add( HINT_FETCH_SIZE );
hints.add( HINT_CACHE_REGION );
Modified:
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java
===================================================================
---
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java 2010-02-22
21:59:43 UTC (rev 18851)
+++
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java 2010-02-22
23:27:16 UTC (rev 18852)
@@ -6,6 +6,7 @@
import javax.persistence.LockTimeoutException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Query;
+import javax.persistence.QueryTimeoutException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -511,8 +512,96 @@
}
}
+ public void testQueryTimeout() throws Exception {
+ EntityManager em = getOrCreateEntityManager();
+ final EntityManager em2 = createIsolatedEntityManager();
+ // TODO: replace dialect instanceof test with a Dialect.hasCapability
+ if ( ! (getDialect() instanceof Oracle10gDialect)) {
+ log.info("skipping testQueryTimeout");
+ return;
+ }
+ Lock lock = new Lock();
+ Thread t = null;
+ FutureTask<Boolean> bgTask = null;
+ final CountDownLatch latch = new CountDownLatch(1);
+ try {
+ lock.setName( "testQueryTimeout" );
+ em.getTransaction().begin();
+ em.persist( lock );
+ em.getTransaction().commit();
+ em.clear();
+
+ em.getTransaction().begin();
+ lock = em.getReference( Lock.class, lock.getId() );
+ em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
+ final Integer id = lock.getId();
+ lock.getName(); // force entity to be read
+ log.info("testQueryTimeout: got write lock");
+
+ bgTask = new FutureTask<Boolean>( new Callable() {
+ public Boolean call() {
+ try {
+ boolean timedOut = false; // true (success) if LockTimeoutException occurred
+ em2.getTransaction().begin();
+ log.info( "testQueryTimeout: (BG) about to read write-locked entity" );
+ // we should block on the following read
+ Lock lock2 = em2.getReference( Lock.class, id );
+ lock2.getName(); // force entity to be read
+ log.info( "testQueryTimeout: (BG) read write-locked entity" );
+ try {
+ // we should block on the following read
+ Query query = em2.createQuery(
+ "select L from Lock_ L where L.id < 10000 ");
+ query.setLockMode( LockModeType.PESSIMISTIC_READ );
+ query.setHint( "javax.persistence.query.timeout", new Integer(500) ); //
1 sec timeout
+ List<Lock> resultList = query.getResultList();
+ String name = resultList.get(0).getName(); // force entity to be read
+ log.info( "testQueryTimeout: name read =" + name );
+ }
+ catch( QueryTimeoutException e) {
+ // success
+ log.info( "testQueryTimeout: (BG) got expected timeout exception" );
+ timedOut = true;
+ }
+ catch ( Throwable e) {
+ log.info( "testQueryTimeout: Expected LockTimeoutException but got unexpected
exception", e );
+ }
+ em2.getTransaction().commit();
+ return new Boolean( timedOut );
+ }
+ finally {
+ latch.countDown(); // signal that we finished
+ }
+ }
+ } );
+ t = new Thread(bgTask);
+ t.setDaemon( true );
+ t.setName( "testQueryTimeout (bg)" );
+ t.start();
+ boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on
success
+ assertTrue( "background test thread finished (lock timeout is broken)",
latchSet);
+ assertTrue( "background test thread timed out on lock attempt",
bgTask.get().booleanValue() );
+ em.getTransaction().commit();
+ }
+ finally {
+ if ( em.getTransaction().isActive() ) {
+ em.getTransaction().rollback();
+ }
+ if ( t != null) { // wait for background thread to finish before deleting entity
+ t.join();
+ }
+ em.getTransaction().begin();
+ lock = em.getReference( Lock.class, lock.getId() );
+ em.remove( lock );
+ em.getTransaction().commit();
+ em.close();
+ em2.close();
+ }
+ }
+
+
public Class[] getAnnotatedClasses() {
return new Class[]{
Lock.class,