There are situations where the time used to execute a transaction takes too long. This can result in unresponsive applications or the appearance that the application has locked up. To control this behavioral aspect of transaction we need to limit the amount of time allocated to a transaction. In this recipe, we will address how this is done.
Using timeouts involves:
setTransactionTimeout
method in the case of BMTDetermining whether timeouts should occur or what the timeout period should be is application-specific. As a result we will not address the first two steps here.
If it is a CMT, we can use the container services to set this limit. The process for doing this is container-specific. For example, in GlassFish the Transaction Timeout period can be configured using the Transaction Service as illustrated in the following screenshot. Notice a Retry Timeout value can also be specified.
For a BMT transaction, we can use the setTransactionTimeout
method. The focus of this recipe is on the use of this method.
Using the setTransactionTimeout
method is pretty simple. The method has a single argument which represents the duration of the time out in seconds. To illustrate its use, modify the changePopulation
method of the BeanManagedPopulationManager
as developed in the Handling transactions manually recipe. Add a call to the setTransactionTimeout
with an argument of 10. After the begin
method, add a sleep
method to force the current thread to sleep for 20 seconds.
public void changePopulation(String cityName, long count) throws SystemException { try { System.out.println("Executing changePopulation"); userTransaction.setTransactionTimeout(10); userTransaction.begin(); System.out.println("Transaction State: " + getTransactionStateString(userTransaction.getStatus())); Thread.sleep(20000); Query query = em.createQuery( "UPDATE City c " + "SET c.population = c.population+:count " + "WHERE c.name = :cityName"); query.setParameter("count", count); query.setParameter("cityName", cityName); int result = query.executeUpdate(); userTransaction.commit(); System.out.println("result: " + result); System.out.println("--- end changePopulation"); } catch (Exception e) { System.out.println("Transaction State: " + getTransactionStateString(userTransaction.getStatus())); } }
Modify the PopulationServlet's processRequest
method. Replace the body of the try block with the following:
clearTables(); populationManager.addCity("Tokyo", "Japan", 32450000); try { bean.changePopulation("Tokyo", 1000); } catch (SystemException ex) { System.out.println("SystemException"); Logger.getLogger(PopulationServlet.class.getName()) .log(Level.SEVERE, null, ex); } List<City> cities = cityFacade.findAll(); out.println("<html>"); out.println("<head>"); out.println("<title>Servlet PopulationServlet</title>"); out.println("</head>"); out.println("<body>"); for (City c : cities) { out.println("<h5>Rio: " + c.getName() + " - " + c.getPopulation() + "</h5>"); } out.println("</body>"); out.println("</html>");
Execute the PopulationServlet
. It will pause and after 20 seconds the output of the servlet will show that the population has not been updated. In the console you will see the following output:
...
INFO: Executing changePopulation
INFO: Transaction State: STATUS_ACTIVE: The transaction is active
INFO: Transaction State: STATUS_NO_TRANSACTION: There is no transaction
...
The setTransactionTimeout
argument is expressed in seconds. The value of 10 meant it should not wait for more than 10 seconds before rolling back the transaction.
To test the method, we used the sleep
method to suspend the current thread for 20 seconds. This argument is expressed in milliseconds so we used a value of 20000 to direct the method to sleep for 20 seconds.
In the console output, the transaction's status was displayed as explained in the Handling transactions manually recipe. It reflected an initial active transaction but when the exception was thrown, the status indicated that a transaction was no longer present.
3.22.216.254