/**
* Copyright (c) 2005-2009 springside.org.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* $Id$
*/
package org.springside.examples.showcase.queue;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springside.modules.utils.ThreadUtils;
import org.springside.modules.utils.ThreadUtils.CustomizableThreadFactory;
/**
* 单线程消费Queue中消息的任务基类.
*
* 定义了QueueConsumer的启动关闭流程.
*
* TODO:支持多线程执行.
*
* @see QueuesHolder
*
* @author calvin
*/
@SuppressWarnings("unchecked")
public abstract class QueueConsumer implements Runnable {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected String queueName;
protected int shutdownTimeout = Integer.MAX_VALUE;
protected boolean persistence = true;
protected String persistencePath = System.getProperty("java.io.tmpdir") + File.separator + "queue";
protected Object persistenceLock = new Object(); //用于在backup与restore间等待的锁.
protected BlockingQueue queue;
protected ExecutorService executor;
/**
* 任务所消费的队列名称.
*/
public void setQueueName(String queueName) {
this.queueName = queueName;
}
/**
* 停止任务时最多等待的时间, 单位为毫秒.
*/
public void setShutdownTimeout(int shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
/**
* 在JVM关闭时是否需要持久化未完成的消息到文件.
*/
public void setPersistence(boolean persistence) {
this.persistence = persistence;
}
/**
* 系统关闭时将队列中未处理的消息持久化到文件的目录,默认为系统临时文件夹下的queue目录.
*/
public void setPersistencePath(String persistencePath) {
this.persistencePath = persistencePath;
}
/**
* 任务初始化函数.
*/
@PostConstruct
public void start() throws IOException, ClassNotFoundException, InterruptedException {
queue = QueuesHolder.getQueue(queueName);
if (persistence) {
synchronized (persistenceLock) {
restoreQueue();
}
}
executor = Executors.newSingleThreadExecutor(new CustomizableThreadFactory("Queue Consumer-" + queueName));
executor.execute(this);
}
/**
* 任务结束函数.
*/
@PreDestroy
public void stop() throws IOException {
try {
ThreadUtils.normalShutdown(executor, shutdownTimeout, TimeUnit.MILLISECONDS);
} finally {
if (persistence) {
synchronized (persistenceLock) {
backupQueue();
}
}
}
}
/**
* 保存队列中的消息到文件.
*/
protected void backupQueue() throws IOException {
List list = new ArrayList();
queue.drainTo(list);
if (!list.isEmpty()) {
ObjectOutputStream oos = null;
try {
File file = new File(getPersistenceDir(), getPersistenceFileName());
oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
for (Object message : list) {
oos.writeObject(message);
}
logger.info("队列{}已持久化{}个消息到{}", new Object[] { queueName, list.size(), file.getAbsolutePath() });
} finally {
if (oos != null) {
oos.close();
}
}
} else {
logger.debug("队列{}为空,不需要持久化 .", queueName);
}
}
/**
* 载入持久化文件中的消息到队列.
*/
protected void restoreQueue() throws ClassNotFoundException, IOException, InterruptedException {
ObjectInputStream ois = null;
File file = new File(getPersistenceDir(), getPersistenceFileName());
if (file.exists()) {
try {
ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)));
int count = 0;
while (true) {
try {
Object message = ois.readObject();
queue.put(message);
count++;
} catch (EOFException e) {
break;
}
}
logger.info("队列{}已从{}中恢复{}个消息.", new Object[] { queueName, file.getAbsolutePath(), count });
} finally {
if (ois != null) {
ois.close();
}
}
file.delete();
} else {
logger.debug("队列{}的持久化文件{}不存在", queueName, file.getAbsolutePath());
}
}
/**
* 获取持久化文件路径.
* 持久化文件默认路径为java.io.tmpdir/queue/队列名.
* 如果java.io.tmpdir/queue/目录不存在,会进行创建.
*/
protected File getPersistenceDir() {
File parentDir = new File(persistencePath + File.separator);
if (!parentDir.exists()) {
parentDir.mkdirs();
}
return parentDir;
}
/**
* 获取持久化文件的名称,默认为queueName,可重载.
*/
protected String getPersistenceFileName() {
return queueName;
}
}