­

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





Free users please don't remove our link. Get the code again.

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