QuartzTriggerHandle set to null in database on second job run
ssarver Mar 17, 2010 4:50 PMAfter upgrading to Quartz 1.7.3 on jboss-5.1.0.GA, I changed
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
in jboss-seam-2.2.0.GA/examples/quartz/resources/seam.quartz.properties
to use the following new configuration that uses a persistent job store,
I noticed that calling handle.cancel() in PaymentHome always throws an exception, because the
QuartzTriggerHandle handle is set to null in the row of the Payment table in the database when Quartz runs the corresponding job the second time.
So, when the cancel() method is called in PaymentHome, it retrieves a null handle.
Why is the LOB handle set to null on the second job run? Does the handle need a scope qualifier, so it will
not behave in this transient way?
What is a good alternate approach to use in this example to cancel a recurring payment?
jboss-seam-2.2.0.GA/examples/quartz/src/org/jboss/seam/example/quartz/PaymentHome.java
/////////////////////////////////////////////////////////////////
@Name("paymentHome")
public class PaymentHome
extends EntityHome<Payment>
{
@RequestParameter Long paymentId;
@In PaymentProcessor processor;
@Logger Log log;
@Transactional
public void cancel() {
Payment payment = getInstance();
QuartzTriggerHandle handle = payment.getQuartzTriggerHandle();
payment.setQuartzTriggerHandle(null);
payment.setActive(false);
try
{
handle.cancel();
}
catch (Exception nsole)
{
FacesMessages.instance().add("Payment already processed");
}
}
...
/////////////////////////////////////////////////////////////////
jboss-seam-2.2.0.GA/examples/quartz/src/org/jboss/seam/example/quartz/PaymentProcessor.java
/////////////////////////////////////////////////////////////////
@Name("processor")
@AutoCreate
public class PaymentProcessor {
@In
EntityManager entityManager;
@Logger Log log;
@Asynchronous
@Transactional
public QuartzTriggerHandle schedulePayment(@Expiration Date when,
@IntervalDuration Long interval,
@FinalExpiration Date stoptime,
Payment payment)
{
payment = entityManager.merge(payment);
log.info("[#0] Processing payment #1", System.currentTimeMillis(), payment.getId());
if (payment.getActive()) {
BigDecimal balance = payment.getAccount().adjustBalance(payment.getAmount().negate());
log.info(":: balance is now #0", balance);
payment.setLastPaid(new Date());
if (payment.getPaymentFrequency().equals(Payment.Frequency.ONCE)) {
payment.setActive(false);
}
}
return null;
}
...
/////////////////////////////////////////////////////////////////
New:
jboss-seam-2.2.0.GA/examples/quartz/resources/seam.quartz.properties
/////////////////////////////////////////////////////////////////
org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.xaTransacted = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 4
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = QUARTZ
org.quartz.dataSource.QUARTZ.jndiURL = java:/QuartzDS
org.quartz.jobStore.nonManagedTXDataSource = QUARTZ_NO_TX
org.quartz.dataSource.QUARTZ_NO_TX.jndiURL = java:/QuartzNoTxDS
///////////////////////////////////////////////////////////////
quartz-ds.xml
//////////////////////////////////////////////////////////////////////
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>QuartzDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/quartz</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>******</user-name>
<password>*******</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
<no-tx-datasource>
<jndi-name>QuartzNoTxDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/quartz</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>*****</user-name>
<password>******</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</no-tx-datasource>
</datasources>
//////////////////////////////////////////////////////////////////////////////////
jboss-seam-2.2.0.GA/examples/quartz/resources/META-INF/persistence.xml
/////////////////////////////////////////////////////////////////////////
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="default">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/seampayEntityManagerFactory"/>
</properties>
</persistence-unit>
<persistence-unit name="local-tx-ds">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/QuartzDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/seampayEntityManagerFactory"/>
</properties>
</persistence-unit>
<persistence-unit name="no-tx-ds">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/QuartzNoTxDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/seampayEntityManagerFactory"/>
</properties>
</persistence-unit>
</persistence>
/////////////////////////////////////////////////////////////////////////
Database table in MySQL:
CREATE TABLE `quartz`.`Payment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`active` bit(1) NOT NULL,
`amount` decimal(19,2) NOT NULL,
`createdDate` datetime NOT NULL,
`lastPaid` datetime DEFAULT NULL,
`payee` varchar(255) NOT NULL,
`paymentCron` varchar(255) DEFAULT NULL,
`paymentDate` datetime NOT NULL,
`paymentEndDate` datetime DEFAULT NULL,
`paymentFrequency` int(11) DEFAULT NULL,
`quartzTriggerHandle` longblob,
`account_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK3454C9E6A3DBB9FA` (`account_id`),
CONSTRAINT `FK3454C9E6A3DBB9FA` FOREIGN KEY (`account_id`) REFERENCES `Account` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
in jboss-seam-2.2.0.GA/examples/quartz/resources/seam.quartz.properties
to use the following new configuration that uses a persistent job store,
I noticed that calling handle.cancel() in PaymentHome always throws an exception, because the
QuartzTriggerHandle handle is set to null in the row of the Payment table in the database when Quartz runs the corresponding job the second time.
So, when the cancel() method is called in PaymentHome, it retrieves a null handle.
Why is the LOB handle set to null on the second job run? Does the handle need a scope qualifier, so it will
not behave in this transient way?
What is a good alternate approach to use in this example to cancel a recurring payment?
jboss-seam-2.2.0.GA/examples/quartz/src/org/jboss/seam/example/quartz/PaymentHome.java
/////////////////////////////////////////////////////////////////
@Name("paymentHome")
public class PaymentHome
extends EntityHome<Payment>
{
@RequestParameter Long paymentId;
@In PaymentProcessor processor;
@Logger Log log;
@Transactional
public void cancel() {
Payment payment = getInstance();
QuartzTriggerHandle handle = payment.getQuartzTriggerHandle();
payment.setQuartzTriggerHandle(null);
payment.setActive(false);
try
{
handle.cancel();
}
catch (Exception nsole)
{
FacesMessages.instance().add("Payment already processed");
}
}
...
/////////////////////////////////////////////////////////////////
jboss-seam-2.2.0.GA/examples/quartz/src/org/jboss/seam/example/quartz/PaymentProcessor.java
/////////////////////////////////////////////////////////////////
@Name("processor")
@AutoCreate
public class PaymentProcessor {
@In
EntityManager entityManager;
@Logger Log log;
@Asynchronous
@Transactional
public QuartzTriggerHandle schedulePayment(@Expiration Date when,
@IntervalDuration Long interval,
@FinalExpiration Date stoptime,
Payment payment)
{
payment = entityManager.merge(payment);
log.info("[#0] Processing payment #1", System.currentTimeMillis(), payment.getId());
if (payment.getActive()) {
BigDecimal balance = payment.getAccount().adjustBalance(payment.getAmount().negate());
log.info(":: balance is now #0", balance);
payment.setLastPaid(new Date());
if (payment.getPaymentFrequency().equals(Payment.Frequency.ONCE)) {
payment.setActive(false);
}
}
return null;
}
...
/////////////////////////////////////////////////////////////////
New:
jboss-seam-2.2.0.GA/examples/quartz/resources/seam.quartz.properties
/////////////////////////////////////////////////////////////////
org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.xaTransacted = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 4
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = QUARTZ
org.quartz.dataSource.QUARTZ.jndiURL = java:/QuartzDS
org.quartz.jobStore.nonManagedTXDataSource = QUARTZ_NO_TX
org.quartz.dataSource.QUARTZ_NO_TX.jndiURL = java:/QuartzNoTxDS
///////////////////////////////////////////////////////////////
quartz-ds.xml
//////////////////////////////////////////////////////////////////////
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>QuartzDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/quartz</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>******</user-name>
<password>*******</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
<no-tx-datasource>
<jndi-name>QuartzNoTxDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/quartz</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>*****</user-name>
<password>******</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</no-tx-datasource>
</datasources>
//////////////////////////////////////////////////////////////////////////////////
jboss-seam-2.2.0.GA/examples/quartz/resources/META-INF/persistence.xml
/////////////////////////////////////////////////////////////////////////
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="default">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/seampayEntityManagerFactory"/>
</properties>
</persistence-unit>
<persistence-unit name="local-tx-ds">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/QuartzDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/seampayEntityManagerFactory"/>
</properties>
</persistence-unit>
<persistence-unit name="no-tx-ds">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/QuartzNoTxDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/seampayEntityManagerFactory"/>
</properties>
</persistence-unit>
</persistence>
/////////////////////////////////////////////////////////////////////////
Database table in MySQL:
CREATE TABLE `quartz`.`Payment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`active` bit(1) NOT NULL,
`amount` decimal(19,2) NOT NULL,
`createdDate` datetime NOT NULL,
`lastPaid` datetime DEFAULT NULL,
`payee` varchar(255) NOT NULL,
`paymentCron` varchar(255) DEFAULT NULL,
`paymentDate` datetime NOT NULL,
`paymentEndDate` datetime DEFAULT NULL,
`paymentFrequency` int(11) DEFAULT NULL,
`quartzTriggerHandle` longblob,
`account_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK3454C9E6A3DBB9FA` (`account_id`),
CONSTRAINT `FK3454C9E6A3DBB9FA` FOREIGN KEY (`account_id`) REFERENCES `Account` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1