My Quotes


When U were born , you cried and the world rejoiced
Live U'r life in such a way that when you go
THE WORLD SHOULD CRY






Tuesday, December 22, 2009

Integrate Hibernate and Quartz (Hibernate alone)

In my project I had a scenario where in I had to integrate Quartz with plain Hibernate for some of the Reports to be scheduled as a batch processing.
When I started integrating I had 2 options either to go with EJBTimer (or) Quartz.

I choose Quartz since I was little more familiar with that.
But when I started the integration I had lot of issues.
Here are some of them. I tried googling but of no luck.

Tried to integrate Quartz with Hibernate (no SPRING). This is the exact implementation


  • I had a stateless Session Bean which calls the job to insert / update a record into the database.This job then internally invokes the Hibernate Sessions to do this.
  • But the problem here is when I do the same way I get an exception stating that "UserTransaction" not bound.
  • I changed the property in the Quartz.properties org.quartz.scheduler.wrapJobExecutionInUserTransaction = true but then also I was not able to synchronize my transactions with that of the hibernate transactions.
  • I some how read it in the post that the file "UserTransactionHelper.java" in the package org.quartz.ee.jta has the following lines of code

    public static final String DEFAULT_USER_TX_LOCATION =    "java:comp/UserTransaction";     
    which needs to be changed to
    public static final String DEFAULT_USER_TX_LOCATION = "UserTransaction";
    


  • The at last I figured out that we need the following properties to be set

        org.quartz.scheduler.wrapJobExecutionInUserTransaction = true
        org.quartz.scheduler.userTransactionURL=UserTransaction
    

    Here is the out an out way to integrate Hibernate Sessions with Quartz.
     SchedulerFactory schedFactory = null;
     Scheduler schedulerObj = null;
     JobDetail jobDetail = null;
     SimpleTrigger trigger = null;
     

  • Download the jar file quartz-all-1.6.5.jar
  • Update the quartz.properties inside this jar file with the 2 properties

        org.quartz.scheduler.wrapJobExecutionInUserTransaction = true
        org.quartz.scheduler.userTransactionURL=UserTransaction
     
  • From the stateless Session Bean
       // First we must get a reference to a scheduler
       schedFactory = new StdSchedulerFactory();
       schedulerObj = schedFactory.getScheduler();
       // computer a time that is on the next round milli second of a second
       Date runTime = TriggerUtils.getNextGivenSecondDate(new Date(), 2);
        // define the job and tie it to our TestUnitJob class
       jobDetail = new JobDetail("QuartzUnitRolesJob","QuartzUnitRolesJob", 
                                  QuartzUnitJob.class);
       // Trigger the job to run on the next round minute
         trigger = new SimpleTrigger("QuartzUnitRolesTrigger","QuartzUnitRolesGroup", 
                                     runTime);
       // pass initialization parameters into the job
         jobDetail.getJobDataMap().put("moduleName", JobTypes.ACL);
       // Tell quartz to schedule the job using our trigger
          schedulerObj.scheduleJob(jobDetail, trigger);
           logger.error(jobDetail.getFullName() + " will run at: " + runTime);
       // Start up the scheduler
            logger.error("------- Started Scheduler -----------------");
            schedulerObj.start();
       // wait long enough so that the scheduler as an opportunity to run the job!
           logger.error("------- Waiting 2 seconds... -------------");
            try {
         // wait 2 seconds to show jobs
            Thread.sleep(2000L);
          // executing...
            } catch (Exception e) {
              } // shut down the scheduler
           logger.error("------- Shutting Down ---------------------");
           schedulerObj.shutdown(true);
           logger.error("------- Shutdown Complete -----------------");
     schedFactory = null;
     schedulerObj = null;
     jobDetail = null;
     trigger = null;
     
  • I created an ENUM to ensure that we can use one job and use the same Quartz job for multiple purpose
    For example
        public enum JobTypes {
                    TEST_UNIT, ACL;
        @Override
           public String toString() {
           switch (this) {
             case TEST_UNIT: {
                 return "testUnit";
             }
             case ACL : {
              return "acl";
             }
             default:{
                 return "Unknown Job Type";
             }
            } // end of switch
    } // end of method
        } // end of class
        
  • As you can see the concept of ENUM is a powerful mechanism. I leave the choice to either use them or use the JobContext and create multiple Quartz Jobs
  • Create the Job
    public class QuartzUnitJob implements Job
        public class QuartzUnitJob implements Job {
          private JobTypes moduleName;
     public JobTypes getModuleName() {
      return moduleName;
     }
     public void setModuleName(JobTypes moduleName) {
      this.moduleName = moduleName;
     }
            public void execute(JobExecutionContext context) throws JobExecutionException {
      JobDataMap data = context.getJobDetail().getJobDataMap();
      JobTypes status = (JobTypes) data.get("moduleName");
                switch (status) {
                            case TEST_UNIT: {
                                        dotask1();
                                         break;
                            }
                            case ACL: {
                                        findAllRoles();
                                        break;
                            }
                 }// close of switch
     } // close of execute method
           private List findAllRoles() { 
             // your business logic goes here
           }
                } // close of class
                
    
  • Now if you go back to point(3) and see the line where I have invoked JobDatMap
    jobDetail.getJobDataMap().put("moduleName", JobTypes.ACL); 
    
  • we can understand that the combination of ENUM and JobDataMap is pretty powerful technique.
  • This ensures that I do not need to create multiple Jobs but still can achieve the same Quartz success.

  • Here are some additional thoughts on the integration
  • Clustering

    Clustering currently only works with the JDBC-Jobstore (JobStoreTX or JobStoreCMT). Features include job fail-over (if the JobDetail's "request recovery" flag is set to true) and load-balancing.

  • JTA Transactions

    As explained in the "JobStores" section of this document, JobStoreCMT allows Quartz scheduling operations to be performed within larger JTA transactions.
    Jobs can also execute within a JTA transaction (UserTransaction) by setting the "org.quartz.scheduler.wrapJobExecutionInUserTransaction" property to "true".
    With this option set, a a JTA transaction will begin() just before the Job's execute method is called, and commit() just after the call to execute terminates.

    For more information on the technical guide here is a good link for the same
    Quartz

  • 1 comment :

    1. Hi ramesh,
      I have a question. Job fire time is on mysql database. For example I need to fire event on predefined time that store in database. Suppose its 2012-05-01 12:00 . So I need to fire event on this exact day and time. How can I do that ? If you can give me help. Its a big help for me.
      Thanks

      ReplyDelete