Wednesday, March 14, 2012

quartz...

Story... ;)
Last week I thought to improve my appzone application. Its a message alert system. Which has some groups and users can join to those groups. Then they will recieves messages every morning. The improvement is i just want to set separatem trigger times for each group. Currently i used quartz in spring context file. I created one schedular factory bean, one trigger bean, and one job bean. So simply what i have to do is create each one of those for each group. But its not possible it another group will added. hmmm.... I need a dynamic and more configurable way. Before explain the solution i'll show my old codes.


    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="alertMessageTrigger"/>
            </list>
        </property>
    </bean>

    <bean id="alertMessageTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <property name="jobDetail" ref="alertMessageTimeTrackJob"/>
        <property name="cronExpression" value="0 0 8 * * ?"/>
    </bean>

    <bean name="alertMessageTimeTrackJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="dailyAlertManager"/>
        <property name="targetMethod" value="startDaily"/>
    </bean>

    <bean id="dailyAlertManager" class="myroad.util.impl.AlertManagerImpl">
        <property name="limit" value="${sms.data.retreive.batch.size}"/>
    </bean>


"AlertManagerImpl" class has a method called "startDaily". So that message will executes by the cron when trigger time occurs. To understand the cron expression please check my old posts.

Ok..  now the solution. I create new class "MySchedularFactoryBean" by extending "SchedularFactoryBean". So to initialize create a bean of that class inside the context file. Now the context.xml has only this.

    <bean class="myroad.say.util.impl.SaySchedulerFactoryBean">
        <property name="groupService" ref="groupService"/>  
    </bean>


This is my class.

public class MySchedulerFactoryBean extends SchedulerFactoryBean {
    private Log logger = LogFactory.getLog(MySchedulerFactoryBean.class);
    private GroupService groupService;
    @Override
    protected void startScheduler(Scheduler scheduler, int startupDelay) throws SchedulerException {
        logger.info("Scheduler starting...");
        try {
            List<Group> groups = groupService.getAllGroups();
            logger.debug("Number of groups in database = " + groups.size());
            for (Group group : groups) {
                if (null == group.getQtzExpression() || group.getQtzExpression().equals("")) {
                    continue;
                }
                JobDetail jobDetail = new JobDetail();
                jobDetail.setName(group.getShortName());
                jobDetail.setJobClass(GroupMsgSendJob.class);

                CronTriggerBean triggerBean = new CronTriggerBean();
                triggerBean.setName(group.getShortName() + "Trigger");
                try {
                    triggerBean.setCronExpression(group.getQtzExpression());
                } catch (ParseException e) {
                    logger.error("Exception while parsing quartz expression for " + group.getShortName() + "Group.", e);
                }
                scheduler.scheduleJob(jobDetail, triggerBean);
            }
        } catch (myroad.say.exception.DBException e) {
            logger.error("DB exception occurs while searching groups for load schedules.", e);
        } catch (SQLException e) {
            logger.error("SQL exception occurs while searching groups for load schedules.", e);
        }
        super.startScheduler(scheduler, startupDelay);
    }

    public void setGroupService(GroupService groupService) {
        this.groupService = groupService;
    }
}

For your convienience I'll show my "Group" pojo.

public class Group {
    private int id;
    private String shortName;
    private String description;
    private List<String> synonyms;
    private String successMessage;
    private String responseMsg1;
    private String qtzExpression;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public List<String> getSynonyms() {
        return synonyms;
    }

    public void setSynonyms(List<String> synonyms) {
        this.synonyms = synonyms;
    }

    public String getShortName() {
        return shortName;
    }

    public void setShortName(String shortName) {
        this.shortName = shortName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getSuccessMessage() {
        return successMessage;
    }

    public void setSuccessMessage(String successMessage) {
        this.successMessage = successMessage;
    }

    public String getResponseMsg1() {
        return responseMsg1;
    }

    public void setResponseMsg1(String responseMsg1) {
        this.responseMsg1 = responseMsg1;
    }

    public String getQtzExpression() {
        return qtzExpression;
    }

    public void setQtzExpression(String qtzExpression) {
        this.qtzExpression = qtzExpression;
    }
}

This one solves my problem. But i have to restart the system after adding new group into the database. mmm... its not a big issue. Because I may add new group once a two or three months ;)

However there is a solution even for that. We can set data source for quarts. Then it will handle the cron expressions dynamically. Also it consumes less proccessing power than my solution.

No comments: