Announcement Announcement Module
Collapse
No announcement yet.
Transactions with 2 data-sources: db4o and Quartz/Derby Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Transactions with 2 data-sources: db4o and Quartz/Derby

    Dear list,

    I am using Db4o via Spring-Db4o and this works like a charm. Now I need to integrate background jobs with persistence and want to use Quartz for this. As Db4o is not yet available for persisting Quartz jobs, I was trying to use Derby. (In another project I successfully used Derby for both Quartz persistence and the persistence of business objects.)

    My question: Is it possible at all to use Db4o and Derby/Quartz with non-CMT Spring transactions?

    Many thanks,
    Kaspar

    P.S. I attach my test case; the rollback which I provoke via the IllegalStateException only happens on the Db4o side but not on the Quartz side. As a result, the second create() at (*) fails with a org.quartz.ObjectAlreadyExistsException.

    Code:
    package org.hbf.basics;
    
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.util.List;
    
    import org.quartz.Job;
    import org.quartz.JobDetail;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.quartz.Scheduler;
    import org.quartz.SchedulerException;
    import org.quartz.StatefulJob;
    import org.springextensions.db4o.support.Db4oDaoSupport;
    import org.springframework.beans.factory.annotation.Required;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
    import org.springframework.transaction.annotation.Transactional;
    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    @ContextConfiguration
    public class SpringDb4oQuartzTest extends AbstractTestNGSpringContextTests
    {
      @Test(groups = {
          "all", "functionality"
      })
      public void simpleTransactionTest() throws FileNotFoundException, IOException, SchedulerException,
          InterruptedException
      {
        PilotService service = (PilotService) applicationContext.getBean("pilotService");
    
        Assert.assertEquals(service.countToms(), 0);
        try
        {
          service.create(true);
          Assert.assertTrue(false);
        }
        catch (IllegalStateException e)
        {
        }
        Assert.assertEquals(service.countToms(), 0);
    
        service.create(false); // (*)
        Assert.assertEquals(service.countToms(), 1);
    
        Thread.sleep(5000);
        List<JobExecutionContext> currentlyExecutingJobs = service.getScheduler().getCurrentlyExecutingJobs();
        System.err.println("Number of running jobs: " + currentlyExecutingJobs.size());
        Assert.assertEquals(currentlyExecutingJobs.size(), 1);
    
        service.remove();
        Assert.assertEquals(service.countToms(), 0);
      }
    
      public static class Pilot
      {
        private String name;
    
        public Pilot(String name)
        {
          this.name = name;
        }
    
        public String getName()
        {
          return name;
        }
    
        public void setName(String name)
        {
          this.name = name;
        }
      }
    
      public static class LongJob implements Job, StatefulJob
      {
        public void execute(JobExecutionContext arg0) throws JobExecutionException
        {
          for (int i = 0; i <= 10; ++i)
          {
            System.err.println("TestJob in phase " + i + " of 10.");
            try
            {
              Thread.sleep(1000);
            }
            catch (InterruptedException e)
            {
              System.err.println("TestJob got interrupted - ignoring.");
              Thread.currentThread().interrupt();
            }
          }
          System.err.println("TestJob finished.");
        }
      }
    
      public interface PilotService
      {
        public void create(boolean fail) throws SchedulerException;
    
        public void remove() throws SchedulerException;
    
        public int countToms();
    
        public Scheduler getScheduler();
      }
    
      public static class PilotServiceImpl extends Db4oDaoSupport implements PilotService
      {
        private Scheduler scheduler;
    
        @Required
        public void setScheduler(Scheduler scheduler)
        {
          this.scheduler = scheduler;
        }
    
        @Transactional(rollbackFor = Exception.class)
        public void create(boolean fail) throws SchedulerException
        {
          // Db4o
          Pilot p = new Pilot("Tom Sawyer");
          getDb4oTemplate().store(p);
    
          // Quartz
          JobDetail jobDetail = new JobDetail("job0", Scheduler.DEFAULT_GROUP, LongJob.class, false, true, true);
          scheduler.addJob(jobDetail, false);
          scheduler.triggerJobWithVolatileTrigger(jobDetail.getName(), jobDetail.getGroup());
    
          if (fail)
          {
            throw new IllegalStateException("Let's fail!");
          }
        }
    
        @Transactional
        public void remove() throws SchedulerException
        {
          Pilot p = new Pilot("Tom Sawyer");
          p = (Pilot) getDb4oTemplate().queryByExample(p).next();
          getDb4oTemplate().delete(p);
          scheduler.deleteJob("job0", Scheduler.DEFAULT_GROUP);
        }
    
        @Transactional(rollbackFor = Exception.class)
        public int countToms()
        {
          Pilot p = new Pilot("Tom Sawyer");
          return getDb4oTemplate().queryByExample(p).size();
        }
    
        public Scheduler getScheduler()
        {
          return scheduler;
        }
      }
    }

  • #2
    Context:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans default-lazy-init="true"
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    	<!-- Quartz with Derby -->
      <bean lazy-init="true" id="uninitializedDerbyDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        	<property name="driverClass"><value>org.apache.derby.jdbc.EmbeddedDriver</value></property>
        	<property name="jdbcUrl"><value>jdbc:derby:target/TestDatabase;create=true;user=me;password=mine</value></property>
        	<property name="user"><value>sa</value></property>
        	<property name="password"><value></value></property>
    		<property name="minPoolSize"><value>2</value></property>
    		<property name="maxPoolSize"><value>20</value></property>
    	</bean>
    	<bean lazy-init="true" id="derbyDataSource"
    		class="org.hbf.task.support.QuartzInitializingDataSourceBean">
    		<property name="dataSource" ref="uninitializedDerbyDataSource"/>
    		<property name="mutipleSqlStatementsProcessor">
    			<bean class="org.hbf.task.support.DerbyMultipleSqlStatementsProcessor" />
    		</property>
    	</bean>
    	<bean lazy-init="true" id="derbySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource"><ref local="derbyDataSource"/></property>
            <property name="hibernateProperties">
    	        <props>
    	        	<prop key="hibernate.connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</prop>
    	        	<prop key="hibernate.connection.url">jdbc:derby:target/TestDatabase;create=true;user=me;password=mine</prop>
    	        	<prop key="hibernate.connection.username">me</prop>
    	        	<prop key="hibernate.connection.password">mine</prop>
        	        <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop>
    				<prop key="hibernate.show_sql">true</prop>
    				<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
    	        </props>
    		</property>
    		<!-- For mappings, etc. -->
    		<!--
    		<property name="configLocation">
    			<value>classpath:hibernate.cfg.xml</value>
    		</property>
    		-->
      </bean>
    	<!--  Scheduler factory for an underlying database (used for testing) -->
    	<bean lazy-init="true" id="derbyScheduler"
    			class="org.hbf.task.SoftShutdownSchedulerFactoryBean">
    		<property name="waitForJobsToCompleteOnShutdown" value="true" />
    	    <property name="triggers">
    	        <list>
    	        </list>
    	    </property>
    		<property name="dataSource">
    			<ref bean="derbyDataSource"/>
    		</property>
    		<property name="transactionManager">
    			<ref bean="transactionManager"/>
    		</property>
    	    <property name="quartzProperties">
    			<props>
    				<prop key="org.quartz.plugin.triggHistory.class">org.quartz.plugins.history.LoggingTriggerHistoryPlugin</prop>
    				<prop key="org.quartz.plugin.triggHistory.triggerFiredMessage">Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss dd/MM/yyyy}</prop>
    				<prop key="org.quartz.plugin.triggHistory.triggerCompleteMessage">Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss dd/MM/yyyy} with resulting trigger instruction code: {9}</prop>
    				<prop key="org.quartz.plugin.jobHistory.class">org.quartz.plugins.history.LoggingJobHistoryPlugin</prop>
    				<prop key="org.quartz.plugin.jobHistory.jobSuccessMessage">Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=OK</prop>
    				<prop key="org.quartz.plugin.jobHistory.jobFailedMessage">Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=ERROR</prop>
    
    				<!-- Only change the following settings if you know what you are doing! -->
    				<prop key="org.quartz.jobStore.class">org.springframework.scheduling.quartz.LocalDataSourceJobStore</prop>
    				<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.CloudscapeDelegate</prop>
    				<prop key="org.quartz.jobStore.lockHandler.class">org.quartz.impl.jdbcjobstore.SimpleSemaphore</prop>
    				<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
    				<prop key="org.quartz.jobStore.selectWithLockSQL">SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?</prop>
    				<prop key="org.quartz.plugin.shutdownhook.class">org.quartz.plugins.management.ShutdownHookPlugin</prop>
    				<prop key="org.quartz.plugin.shutdownhook.cleanShutdown">true</prop>
    			</props>
    		</property>
    		<property name="autoStartup" value="false"/>
    		<property name="applicationContextSchedulerContextKey">
    			<value>applicationContext</value>
    		</property>
    	</bean>
    	<alias alias="scheduler" name="derbyScheduler" />
    	<alias alias="sessionFactory" name="derbySessionFactory" />
    
      <!-- Transaction manager for a single in-memory Db4o database -->
      <bean id="objectContainer" class="org.springextensions.db4o.ObjectContainerFactoryBean">
      	<property name="memoryFile">
      		<bean class="com.db4o.ext.MemoryFile"/>
      	</property>
      </bean>
      <bean id="transactionManager" class="org.springextensions.db4o.Db4oTransactionManager">
          <property name="objectContainer" ref="objectContainer"/>
      </bean>
    
      <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
    		<property name="transactionManager" ref="transactionManager"/>
      	<property name="transactionAttributes">
      		<props>
      			<prop key="*">PROPAGATION_REQUIRED, ISOLATION_READ_COMMITTED</prop>
      		</props>
      	</property>
      </bean>
    
    	<!-- Enable transaction annotations -->
    	<tx:annotation-driven transaction-manager="transactionManager" />
    
    	<!-- Test-specific beans -->
    	<bean id="pilotService" class="org.hbf.basics.SpringDb4oQuartzTest$PilotServiceImpl">
    		<property name="objectContainer" ref="objectContainer"/>
    		<property name="scheduler" ref="scheduler" />
    	</bean>
    </beans>

    Comment


    • #3
      And the error log:

      Code:
      INFO  - LocalDataSourceJobStore    - Removed 0 Volatile Trigger(s).
      INFO  - LocalDataSourceJobStore    - Removed 0 Volatile Job(s).
      INFO  - LocalDataSourceJobStore    - JobStoreCMT initialized.
      INFO  - StdSchedulerFactory        - Quartz scheduler 'derbyScheduler' initialized from an externally provided properties instance.
      INFO  - StdSchedulerFactory        - Quartz scheduler version: null.null.null
      INFO  - QuartzScheduler            - JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@ed723b
      DEBUG - tionSynchronizationManager - Bound value [org.springextensions.db4o.ObjectContainerHolder@5b6354] for key [Memory File] to thread [main]
      DEBUG - tionSynchronizationManager - Initializing transaction synchronization
      DEBUG - tionSynchronizationManager - Clearing transaction synchronization
      DEBUG - tionSynchronizationManager - Removed value [org.springextensions.db4o.ObjectContainerHolder@5b6354] for key [Memory File] from thread [main]
      DEBUG - TransactionAttributeSource - Adding transactional method [countToms] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception]
      DEBUG - tionSynchronizationManager - Bound value [org.springextensions.db4o.ObjectContainerHolder@ca662] for key [Memory File] to thread [main]
      DEBUG - tionSynchronizationManager - Initializing transaction synchronization
      DEBUG - TransactionInterceptor     - Getting transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.countToms]
      DEBUG - TransactionInterceptor     - Completing transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.countToms]
      DEBUG - tionSynchronizationManager - Clearing transaction synchronization
      DEBUG - tionSynchronizationManager - Removed value [org.springextensions.db4o.ObjectContainerHolder@ca662] for key [Memory File] from thread [main]
      DEBUG - TransactionAttributeSource - Adding transactional method [create] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception]
      DEBUG - tionSynchronizationManager - Bound value [org.springextensions.db4o.ObjectContainerHolder@c8bfa4] for key [Memory File] to thread [main]
      DEBUG - tionSynchronizationManager - Initializing transaction synchronization
      DEBUG - TransactionInterceptor     - Getting transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.create]
      DEBUG - tionSynchronizationManager - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@886a64] for key [com.mchange.v2.c3p0.ComboPooledDataSource [ ... ]] to thread [main]
      DEBUG - tionSynchronizationManager - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@886a64] for key [com.mchange.v2.c3p0.ComboPooledDataSource [ ... ]] bound to thread [main]
      DEBUG - tionSynchronizationManager - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@886a64] for key [com.mchange.v2.c3p0.ComboPooledDataSource [ ... ]] bound to thread [main]
      DEBUG - tionSynchronizationManager - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@886a64] for key [com.mchange.v2.c3p0.ComboPooledDataSource [ ... ]] bound to thread [main]
      DEBUG - TransactionInterceptor     - Completing transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.create] after exception: java.lang.IllegalStateException: Let's fail!
      DEBUG - eBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on java.lang.IllegalStateException: Let's fail!
      DEBUG - eBasedTransactionAttribute - Winning rollback rule is: RollbackRuleAttribute with pattern [java.lang.Exception]
      DEBUG - tionSynchronizationManager - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@886a64] for key [com.mchange.v2.c3p0.ComboPooledDataSource [ ... ]] from thread [main]
      DEBUG - tionSynchronizationManager - Clearing transaction synchronization
      DEBUG - tionSynchronizationManager - Removed value [org.springextensions.db4o.ObjectContainerHolder@c8bfa4] for key [Memory File] from thread [main]
      DEBUG - tionSynchronizationManager - Bound value [org.springextensions.db4o.ObjectContainerHolder@79b474] for key [Memory File] to thread [main]
      DEBUG - tionSynchronizationManager - Initializing transaction synchronization
      DEBUG - TransactionInterceptor     - Getting transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.countToms]
      DEBUG - TransactionInterceptor     - Completing transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.countToms]
      DEBUG - tionSynchronizationManager - Clearing transaction synchronization
      DEBUG - tionSynchronizationManager - Removed value [org.springextensions.db4o.ObjectContainerHolder@79b474] for key [Memory File] from thread [main]
      DEBUG - tionSynchronizationManager - Bound value [org.springextensions.db4o.ObjectContainerHolder@203951] for key [Memory File] to thread [main]
      DEBUG - tionSynchronizationManager - Initializing transaction synchronization
      DEBUG - TransactionInterceptor     - Getting transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.create]
      DEBUG - tionSynchronizationManager - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@3f6984] for key [com.mchange.v2.c3p0.ComboPooledDataSource [...] to thread [main]
      DEBUG - tionSynchronizationManager - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@3f6984] for key [com.mchange.v2.c3p0.ComboPooledDataSource [...]] bound to thread [main]
      DEBUG - TransactionInterceptor     - Completing transaction for [org.hbf.basics.SpringDb4oQuartzTest$PilotService.create] after exception: org.quartz.ObjectAlreadyExistsException: Unable to store Job with name: 'job0' and group: 'DEFAULT', because one already exists with this identification.
      DEBUG - eBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on org.quartz.ObjectAlreadyExistsException: Unable to store Job with name: 'job0' and group: 'DEFAULT', because one already exists with this identification.
      DEBUG - eBasedTransactionAttribute - Winning rollback rule is: RollbackRuleAttribute with pattern [java.lang.Exception]
      DEBUG - tionSynchronizationManager - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@3f6984] for key [com.mchange.v2.c3p0.ComboPooledDataSource [...]] from thread [main]
      DEBUG - tionSynchronizationManager - Clearing transaction synchronization
      DEBUG - tionSynchronizationManager - Removed value [org.springextensions.db4o.ObjectContainerHolder@203951] for key [Memory File] from thread [main]
      FAILED: simpleTransactionTest
      org.quartz.ObjectAlreadyExistsException: Unable to store Job with name: 'job0' and group: 'DEFAULT', because one already exists with this identification.
      	at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1075)
      	at org.quartz.impl.jdbcjobstore.JobStoreSupport$4.execute(JobStoreSupport.java:1052)
      	at org.quartz.impl.jdbcjobstore.JobStoreSupport$39.execute(JobStoreSupport.java:3639)
      	at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:244)
      	at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInLock(JobStoreSupport.java:3635)
      	at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1048)
      	at org.quartz.core.QuartzScheduler.addJob(QuartzScheduler.java:814)
      	at org.quartz.impl.StdScheduler.addJob(StdScheduler.java:288)
      	at org.hbf.basics.SpringDb4oQuartzTest$PilotServiceImpl.create(SpringDb4oQuartzTest.java:127)
      	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
      	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
      	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
      	at $Proxy13.create(Unknown Source)
      	at org.hbf.basics.SpringDb4oQuartzTest.simpleTransactionTest(SpringDb4oQuartzTest.java:44)
      	at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.run(AbstractTestNGSpringContextTests.java:140)
      ... Removed 32 stack frames

      Comment

      Working...
      X