Quartz is a popular Java open source job scheduler that supports clustering, that means is can make sure that a job is run on exactly one node in a cluster. Many people run Quartz on WebSphere Application Server but face problems when their Quartz jobs try to access JNDI resources or try to participate in a JTA transaction.
You get an exception with the following text:
javax.naming.ConfigurationException: A JNDI operation on a “java:” name cannot be completed because the server runtime is not able to associate the operation’s thread with any J2EE application component. This condition can occur when the JNDI client using the “java:” name is not executed on the thread of a server application request. Make sure that a J2EE application does not execute JNDI operations on “java:” names within static code blocks or in threads created by that J2EE application. Such code does not necessarily run on the thread of a server application request and therefore is not supported by JNDI operations on “java:” names. [Root exception is javax.naming.NameNotFoundException: Name comp/env/[…] not found in context “java:”.]
IBM’s famous document Using Spring and Hibernate with WebSphere Application Server says about scheduling:
Other packages, such as quartz and the JDK Timer, start unmanaged threads and should be avoided.
The Spring Framework chapter in the WAS 8.0 documentation states the reasons for avoiding unmanaged threads. One point is
Unmanaged threads do not have access to Java EE contextual information.
To make Quartz fully supported on WebSphere you need to make sure that no
unmanaged threads are started. Your jobs then have access to JNDI resources
and can participate in JTA transactions (which require a JNDI lookup to
java:comp/UserTransaction
).
It has long been possible to specify a custom thread pool that delegates to a CommonJ work manager to create managed threads. However, Quartz starts some internal threads from which the actual jobs are started. If the Quartz-internal threads are unmanaged the JEE context is lost and can’t be propagated to the job threads, even if they are managed.
The two issues QTZ-113
and QTZ-194 were fixed in
Quartz 2.1 by introducing a new SPI: org.quartz.spi.ThreadExecutor
. To get
the Quartz-internal threads managed, you need to set two properties in your
Quartz configuration:
org.quartz.threadExecutor.class=org.quartz.commonj.WorkManagerThreadExecutor
org.quartz.threadExecutor.workManagerName=wm/default
Important: Don’t use the java:comp/env
prefix for the JNDI work manager
name. As of Quartz 2.2.1, you’ll get a warning in WebSphere’s SystemOut.log
log file after a couple of minutes:
ThreadMonitor W WSVR0605W: Thread "WorkManager.DefaultWorkManager : 1" (000001eb) has been active for 736400 milliseconds and may be hung. There is/are 1 thread(s) in total in the server that may be hung. at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:196) at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:410) at org.quartz.commonj.DelegatingWork.run(WorkManagerThreadExecutor.java:97) at com.ibm.ws.asynchbeans.J2EEContext$RunProxy.run(J2EEContext.java:265) at java.security.AccessController.doPrivileged(AccessController.java:229) at com.ibm.ws.asynchbeans.J2EEContext.run(J2EEContext.java:1165) at com.ibm.ws.asynchbeans.WorkWithExecutionContextImpl.go(WorkWithExecutionContextImpl.java:199) at com.ibm.ws.asynchbeans.CJWorkItemImpl.run(CJWorkItemImpl.java:236) at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1691)
I have created a bug report with a fix for this in Terracotta’s JIRA:
QTZ-433. You should use
your own ThreadExecutor
implementation until this bug gets fixed in a
future Quartz version.
Integration with the Spring Framework
The Spring Framework offers
support for Quartz
using
SchedulerFactoryBean
:
<bean id="quartzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="taskExecutor" ref="taskExecutor" />
<property name="quartzProperties">
<props>
<prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
<prop key="org.quartz.threadExecutor.class">org.quartz.commonj.WorkManagerThreadExecutor</prop>
<prop key="org.quartz.threadExecutor.workManagerName">wm/default</prop>
</props>
</property>
</bean>
You can set the task executor to a CommonJ work manager so the threads that will be created for your Quartz jobs are managed by the application server:
<bean id="taskExecutor"
class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="resourceRef" value="true" />
<property name="workManagerName" value="wm/default" />
</bean>
A sample project is available from my GitHub repository.