Sunday, October 17, 2010

Java Scheduling with Quartz and Spring

So, moving on from my last post lets do some more generic stuff with Quartz scheduler. First of all let us define a Use case; We need couple of processes which can edit or change a common java collection e.g. Map. To make this work we need a bean defined in the spring, now this bean is a representation of a Map which we need to work on.

Public class MyMap<k,v> {
   private Map<K,V> myMap = new HashMap<K,V>();

           public V get(K key){
              return myMap.get(key);
          }

      public void put(K key, V value){
          myMap.put(key,value);
      }

}

In my case i need a bit more complex map, but you got the point i need a workspace object on which my jobs can work and if it is declared as bean then i can pass the reference of this object to the job.

Note: As i will be working on a common workspace object i need to make sure when i do any changes on this object inside a job they will not be persistent(By default Quartz job are stateless).

What i need is a job which can initialize this map. So, I will create MyJob class where i will pass on the reference to this bean in the spring configuration file like this:


<bean id="myMap" class="com.myproj.MyMap"></bean>

<bean id="createMapJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.myproj.CreateMapJob" />
<property name="jobDataAsMap">
<map>
<entry key="myMap" value-ref="myMap"/>
</map>
</property>
</bean>


After this configuration we are ready to write our CreateMapJob class.

/**
* This class extends the QuartzJobBean class which defines that we will be treating this as a spring
* Quartz Job. It implements a marker interface which defines this Job as Stateful Job.
*/
public class CreateMapJob extends QuartsJobBean implements StatefulJob{

private MyMap myMap;

   public void setMyMap(MyMap myMap){
          this.myMap = myMap;
   }

    protected void executeInternal(JobExecutionContext jobexecutioncontext) throws JobExecutionException                                          
   {
          JobDataMap map = jobexecutioncontext.getJobDetail().getJobDataMap();
           MyMap myMap = (MyMap) map.get("myMap");
           myMap.put("1", "Im a stateful Job !!");
        
   }

}


After this you would have an entry in the bean myMap and this will be persistent, please note you need to declare this job as stateful to save you changes in the bean. The trade off that will remain is that this Job can't be executed in parallel, this will be sychronized. Moving on we need another job which can run parallel to the first job which will read the Data pushed into the map.


<bean id="readMapJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.myproj.ReadMapJob" />
<property name="jobDataAsMap">
<map>
<entry key="myMap" value-ref="myMap"/>
</map>
</property>
</bean>


After defining the Job we need to write the class


public class ReadMapJob extends QuartsJobBean{

private MyMap myMap;

   public void setMyMap(MyMap myMap){
          this.myMap = myMap;
   }

    protected void executeInternal(JobExecutionContext jobexecutioncontext) throws JobExecutionException                                           
   {
          JobDataMap map = jobexecutioncontext.getJobDetail().getJobDataMap();
           MyMap myMap = (MyMap) map.get("myMap");
           System.out.println(myMap.get("1"));
          
   }

}

In this way you can have Quartz scheduler share data between them. ofcourse in case of any practical scheduler job you would need triggers which can be configured to fire a job on scheduled time. More on that later....

No comments: