com.bleum.canton.jms.scheduler.AbstractJMSScheduler.java Source code

Java tutorial

Introduction

Here is the source code for com.bleum.canton.jms.scheduler.AbstractJMSScheduler.java

Source

/* <Bleum Inc. Copyright>
 * Copyright (C) 2013 Bleum Inc.
 * All Rights Reserved.  No use, copying or distribution of this
 * work may be made except in accordance with a valid license
 * agreement from Bleum Inc..  This notice must be
 * included on all copies, modifications and derivatives of this
 * work.
 *
 * Bleum Inc. MAKES NO REPRESENTATIONS OR WARRANTIES
 * ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. Belum Inc. 
 * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 * </Bleum Inc. Copyright>
 */
package com.bleum.canton.jms.scheduler;

import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.commons.lang3.StringUtils;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

import com.bleum.canton.jms.constant.JMSTaskConstant;
import com.bleum.canton.jms.dao.IJMSTaskDao;
import com.bleum.canton.jms.entity.JMSTask;

/**
 * 
 * Description: JMS scheduler can run by fixed delay, fix rate and cron. when
 * it's triggered, it collects the related tasks, and sends them by configured
 * threads.
 * 
 * @author jay.li
 * @created Feb 19, 2014 10:30:44 AM
 * 
 */
public abstract class AbstractJMSScheduler implements Runnable {

    /**
     * taks type
     */
    private int taskType;

    /**
     * fixed delay milliseconds
     */
    private long fixedDelay;

    /**
     * fixed rate milliseconds, if fixedDelay is set, this will be ignored.
     */
    private long fixedRate;

    /**
     * cron, if fixedDelay or fixedRate is set, this will be ignored.
     */
    private String cron;

    /**
     * Initial delay, after which the scheduler is triggered.
     */
    private long initialDelay;

    /**
     * Max allowed thread number.
     */
    private int threads;

    /**
     * Max retry.
     */
    private int maxRetry;

    /**
     * Max acknowledge retry.
     */
    private int maxAckRetry;

    /**
     * Max tasks one thread can do.
     */
    private int maxTasksPerThread;

    /**
     * Client acknowledge type
     */
    private int clientAck;

    /**
     * Spring wrapper for jdk scheduledExecutor
     */
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Resource(name = "jmsTaskDao")
    private IJMSTaskDao jmsTaskDao;

    /**
     * Using these future can help monitoring results of task threads
     */
    private Collection<Future<?>> runFutures;

    /**
     * JMS sender. If don't send jms, don't set this field and override
     * <tt>sendMessage</tt>
     */
    private JmsTemplate jmsSender;

    /**
     * Reply queue for client acknowledge.
     */
    private Queue replyQueue;

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // re-initialize the futures
        runFutures = new LinkedList<Future<?>>();

        // load tasks
        List<JMSTask> tasks = loadTasks();

        // execute tasks
        excuteTasks(tasks);

        // Wait for all tasks done. If quit immediately, there's a chance next
        // scheduler runs, and will cause status conflict. FixedDelay and
        // fixedRate guarantee Spring scheduler run after the previous one.
        for (Future<?> future : runFutures) {
            try {
                future.get();
            } catch (InterruptedException e) {
                // no need to handle
            } catch (ExecutionException e) {
                // no need to handle
            }
        }
    }

    /**
     * For the XML message fro JMS sending.
     * 
     * @param task
     * @return
     */
    abstract protected String formMessage(JMSTask task);

    /**
     * Send JMS message. Can be override.
     * 
     * @param task
     */
    protected void sendMessage(final JMSTask task) {
        if (jmsSender == null) {
            throw new RuntimeException("jmsSender is null.");
        }
        MessageCreator mc = new MessageCreator() {

            public Message createMessage(Session session) throws JMSException {
                TextMessage om = session.createTextMessage();
                om.setText(formMessage(task));
                om.setIntProperty("clientAck", clientAck);
                if (clientAck == JMSTaskConstant.CLIENT_ACKNOWLEDGE) {
                    om.setLongProperty("taskId", task.getId());
                    om.setJMSReplyTo(replyQueue);
                }
                return om;
            }
        };
        jmsSender.send(mc);
    }

    /**
     * Auto start the scheduler after all properties set.
     */
    @PostConstruct
    private void start() {
        // initialize Spring ThreadPoolTaskScheduler
        threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.initialize();

        // set properties
        setProperties();

        // Spring ThreadPoolTaskScheduler
        Date startDate = new Date(System.currentTimeMillis() + initialDelay);
        if (fixedDelay > 0) {
            threadPoolTaskScheduler.scheduleWithFixedDelay(this, startDate, fixedDelay);
        } else if (fixedRate > 0) {
            threadPoolTaskScheduler.scheduleAtFixedRate(this, startDate, fixedDelay);
        } else if (StringUtils.isNotBlank(cron)) {
            threadPoolTaskScheduler.schedule(this, new CronTrigger(cron));
        } else {
            threadPoolTaskScheduler.scheduleWithFixedDelay(this, startDate, JMSTaskConstant.DEFAULT_FIXED_DELAY);
        }
    }

    /**
     * Set properties
     */
    private void setProperties() {
        if (threads <= 0) {
            threads = JMSTaskConstant.DEFAULT_MAX_THREADS;
        }
        if (maxTasksPerThread <= 0) {
            maxTasksPerThread = JMSTaskConstant.DEFAULT_MAX_TASKS_PER_THREADS;
        }
        if (initialDelay <= 0) {
            initialDelay = JMSTaskConstant.DEFAULT_INITIAL_DELAY;
        }
        if (maxRetry <= 0) {
            maxRetry = JMSTaskConstant.DEFAULT_MAX_RETRY;
        }
        if (maxAckRetry <= 0) {
            maxAckRetry = JMSTaskConstant.DEFAULT_MAX_RETRY;
        }
        if (clientAck <= 0) {
            clientAck = JMSTaskConstant.NO_ACKNOWLEDGE;
        } else if (clientAck == JMSTaskConstant.CLIENT_ACKNOWLEDGE) {
            if (replyQueue == null) {
                throw new RuntimeException("replyQueue must not be null if using client acknoledge mode.");
            }
        }
    }

    /**
     * Stop Spring ThreadPoolTaskScheduler
     */
    @PreDestroy
    private void stop() {
        if (threadPoolTaskScheduler != null) {
            threadPoolTaskScheduler.shutdown();
        }
    }

    /**
     * Update numbers of new tasks to processing, and return the number of tasks
     * for processing.
     * 
     * @return
     */
    private List<JMSTask> loadTasks() {
        jmsTaskDao.updateTaskForProcessing(this.taskType, threads * maxTasksPerThread);
        return jmsTaskDao.findTaskForProcessing(this.taskType, threads * maxTasksPerThread);
    }

    /**
     * Run the tasks using threads.
     * 
     * @param tasks
     */
    private void excuteTasks(List<JMSTask> tasks) {
        int tSize = tasks.size();
        if (tSize <= 0) {
            return;
        }
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, threads, 60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(maxTasksPerThread * threads));
        for (final JMSTask task : tasks) {
            final int mRetry = this.maxRetry;
            final int mAckRetry = this.maxAckRetry;
            runFutures.add(executor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        sendMessage(task);
                        if (clientAck == JMSTaskConstant.NO_ACKNOWLEDGE) {
                            jmsTaskDao.updateTaskCompeleted(task.getId());
                        } else if (clientAck == JMSTaskConstant.CLIENT_ACKNOWLEDGE) {
                            // if sent maxRetry times, won't wait for
                            // acknowledge, just complete it.
                            jmsTaskDao.updateTaskProcessed(task, mAckRetry);
                        }
                    } catch (Exception e) {
                        // if retried sending maxRetry times, make it fatal, and
                        // no longer retry.
                        task.setLastError(e.getClass().getSimpleName() + ":" + e.getMessage());
                        jmsTaskDao.updateErrorTask(task, mRetry);
                    }
                }
            }));
        }
    }

    /**
     * @return Returns the taskType.
     */
    public int getTaskType() {
        return taskType;
    }

    /**
     * @param taskType
     *            The taskType to set.
     */
    public void setTaskType(int taskType) {
        this.taskType = taskType;
    }

    /**
     * @return Returns the fixedDelay.
     */
    public long getFixedDelay() {
        return fixedDelay;
    }

    /**
     * @param fixedDelay
     *            The fixedDelay to set.
     */
    public void setFixedDelay(long fixedDelay) {
        this.fixedDelay = fixedDelay;
    }

    /**
     * @return Returns the fixedRate.
     */
    public long getFixedRate() {
        return fixedRate;
    }

    /**
     * @param fixedRate
     *            The fixedRate to set.
     */
    public void setFixedRate(long fixedRate) {
        this.fixedRate = fixedRate;
    }

    /**
     * @return Returns the cron.
     */
    public String getCron() {
        return cron;
    }

    /**
     * @param cron
     *            The cron to set.
     */
    public void setCron(String cron) {
        this.cron = cron;
    }

    /**
     * @return Returns the initialDelay.
     */
    public long getInitialDelay() {
        return initialDelay;
    }

    /**
     * @param initialDelay
     *            The initialDelay to set.
     */
    public void setInitialDelay(long initialDelay) {
        this.initialDelay = initialDelay;
    }

    /**
     * @return Returns the threads.
     */
    public int getThreads() {
        return threads;
    }

    /**
     * @param threads
     *            The threads to set.
     */
    public void setThreads(int threads) {
        this.threads = threads;
    }

    /**
     * @return Returns the maxRetry.
     */
    public int getMaxRetry() {
        return maxRetry;
    }

    /**
     * @param maxRetry
     *            The maxRetry to set.
     */
    public void setMaxRetry(int maxRetry) {
        this.maxRetry = maxRetry;
    }

    /**
     * @return Returns the maxTasksPerThread.
     */
    public int getMaxTasksPerThread() {
        return maxTasksPerThread;
    }

    /**
     * @param maxTasksPerThread
     *            The maxTasksPerThread to set.
     */
    public void setMaxTasksPerThread(int maxTasksPerThread) {
        this.maxTasksPerThread = maxTasksPerThread;
    }

    /**
     * @return Returns the clientAck.
     */
    public int getClientAck() {
        return clientAck;
    }

    /**
     * @param clientAck
     *            The clientAck to set.
     */
    public void setClientAck(int clientAck) {
        this.clientAck = clientAck;
    }

    /**
     * @return Returns the runFutures.
     */
    public Collection<Future<?>> getRunFutures() {
        return runFutures;
    }

    /**
     * @return Returns the jmsSender.
     */
    public JmsTemplate getJmsSender() {
        return jmsSender;
    }

    /**
     * @param jmsSender
     *            The jmsSender to set.
     */
    public void setJmsSender(JmsTemplate jmsSender) {
        this.jmsSender = jmsSender;
    }

    /**
     * @return Returns the replyQueue.
     */
    public Queue getReplyQueue() {
        return replyQueue;
    }

    /**
     * @param replyQueue
     *            The replyQueue to set.
     */
    public void setReplyQueue(Queue replyQueue) {
        this.replyQueue = replyQueue;
    }

    /**
     * @return Returns the maxAckRetry.
     */
    public int getMaxAckRetry() {
        return maxAckRetry;
    }

    /**
     * @param maxAckRetry
     *            The maxAckRetry to set.
     */
    public void setMaxAckRetry(int maxAckRetry) {
        this.maxAckRetry = maxAckRetry;
    }

}