使用场景:

在平常web开发过程中,有时操作员要做一个后台会运行很长时间的任务(如上传一个大文件到后台处理),而此时前台页面仍需要给用户及时的进度信息反馈,同时还要避免前台页面超时。

框架介绍:

本架构采用Struts+Spring+AJAX(jquery)方式实现,前台提交任务到后台,然后通过AJAX方式周期性获取任务进度,展示给用户看。

本框架支持提交任务、查看任务进展、停止任务、删除任务。

使用java线程池的技术来执行任务,避免不停的生成新的线程;

代码下载(内含使用样例):

https://github.com/jerrymousecn/daemo_tasks

下载包说明:

1)demo/daemonTasks.war为tomcat发布包(zip格式),可以直接放到tomcat的webapps目录;

2)demo/demo_source目录是eclipse样例项目代码

3)source目录是本框架源代码

主要代码:

1.DaemonTask.java

后台任务基础类

package cn.jerry.tools.tasks;

import java.util.Date;

public abstract class DaemonTask implements Runnable {
private String taskID;
private String taskName;
private String taskDesc;
private boolean isStartedFlag = false;;
private boolean isTerminated = false;;
private int progress;
private Date startTime;
private Date terminatedTime;
protected boolean toStopFlag = false; protected abstract void execute(); protected void setProgress(int progress) {
this.progress = progress;
} public void run() {
this.isStartedFlag = true;
this.execute();
this.isTerminated = true;
this.terminatedTime = new Date();
} public String getTaskID() {
return taskID;
} public void setTaskID(String taskID) {
this.taskID = taskID;
} public boolean isStarted() {
return isStartedFlag;
} public boolean getIsTerminated() {
return isTerminated;
}
public void setToStopFlag(boolean toStopFlag) {
this.toStopFlag = toStopFlag;
} public Date getStartTime() {
return startTime;
} public Date getTerminatedTime() {
return terminatedTime;
} public int getProgress() {
return progress;
} public String getTaskName() {
return taskName;
} public void setTaskName(String taskName) {
this.taskName = taskName;
} public String getTaskDesc() {
return taskDesc;
} public void setTaskDesc(String taskDesc) {
this.taskDesc = taskDesc;
} }

2.TaskManager.java

后台任务管理类,支持添加任务,停止任务,删除任务,查看任务,对已完成任务进行定期清理,获取任务进度

package cn.jerry.tools.tasks;

import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class TaskManager {
private static TaskManager taskManager = new TaskManager();
private ExecutorService executorService = Executors.newCachedThreadPool();
private ConcurrentMap<String, DaemonTask> taskMap = new ConcurrentHashMap<String, DaemonTask>();
private final int TASK_NOT_FOUND = -1;
private TaskCleaner taskCleaner = new TaskCleaner(taskMap);
private Timer timer = new Timer();
private final int CLEAN_PERIOD = 200000; //in milliseconds private String getUniqueID() {
return UUID.randomUUID().toString();
} private TaskManager() {
startTerminatedTaskCleanTimer();
} private void startTerminatedTaskCleanTimer() {
timer.schedule(taskCleaner, 0, CLEAN_PERIOD);
} private void stopTerminatedTaskCleanTimer() {
timer.cancel();
} public static TaskManager getInstance() {
return taskManager;
} public synchronized String addTaskAndStart(DaemonTask task) {
String taskID = getUniqueID();
task.setTaskID(taskID);
taskMap.put(taskID, task);
executorService.execute(task);
return taskID;
} public synchronized void stopTask(String taskID) {
DaemonTask task = getTask(taskID);
if (task != null) {
task.setToStopFlag(true);
}
}
public synchronized void delFinishedTask(String taskID) {
DaemonTask task = getTask(taskID);
if (task != null) {
if (task.getIsTerminated()) {
taskMap.remove(taskID);
}
}
} public DaemonTask getTask(String taskID) {
return taskMap.get(taskID);
}
public Map<String, DaemonTask> getTasksForView() {
Map<String, DaemonTask> map = Collections.unmodifiableMap(taskMap);
return map;
} public int getProgress(String taskID) {
int progress = TASK_NOT_FOUND;
DaemonTask task = getTask(taskID);
if (task != null) {
progress = task.getProgress();
}
return progress;
} } class TaskCleaner extends TimerTask {
private ConcurrentMap<String, DaemonTask> taskMap; public TaskCleaner(ConcurrentMap<String, DaemonTask> taskMap) {
this.taskMap = taskMap;
} @Override
public void run() {
for (Entry<String, DaemonTask> entry : taskMap.entrySet()) {
String taskID = entry.getKey();
DaemonTask task = entry.getValue();
if (task.getIsTerminated()) {
Date terminatedTime = task.getTerminatedTime();
Date now = new Date(); long timeOffset = getTimeOffset(terminatedTime, now);
if (timeOffset > 10000) {
taskMap.remove(taskID);
System.out.println("task: " + taskID + " is removed");
}
}
}
} private long getTimeOffset(Date date1, Date date2) {
long time1 = date1.getTime();
long time2 = date2.getTime();
long offset = time2 - time1;
if (offset < 0) {
offset = -offset;
}
return offset;
}
}

3.任务样例TestTask.java

关键点需要注意: 方法中需要自行计算进度(完成时,进度应设置为100),并且要对toStopFlag进行判断,用于终止任务。

package cn.jerry.tools.tasks;

public class TestTask extends DaemonTask {

	@Override
protected void execute() {
for (int i = 0; i < 10; i++) {
if(this.toStopFlag==false)
{
int progress = (i + 1) * 100 / 10;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setProgress(progress);
}
}
}
}

4.前台查询状态页面getTaskResult.jsp:

<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
<link rel="stylesheet" href="css/jquery-ui.css">
<script src="js/jquery-ui.js"></script>
<link rel="stylesheet" href="css/style.css">
<script type="text/javascript">
var intervalId;
function trim(str){
return str.replace(/(^\s*)|(\s*$)/g, "");
}
function getprogress() {
htmlobj=$.ajax({url:"getTaskProgress.jsp?taskid="+'<s:property value="taskid"/>',cache:false,async:false});
str = trim(htmlobj.responseText);
$("#myDiv").html(str);
$( "#progressbar" ).progressbar({
value: parseInt(str)
});
if(parseInt(str)>=100)
{
clearInterval(intervalId);
}
} $(document).ready(function(){
intervalId = setInterval(getprogress, 1000);
}); </script>
</head>
<body>
Task [<s:property value="taskName"/>]progress:
<div id="progressbar"></div>
<div id="myDiv"></div>
</body>
</html>

5.相关截图

1)进度页面截图:

2)查看任务列表

声明:

本框架中使用的进度条代码实现来自以下网站,版权不属于本人,本框架仅用于学习交流。

http://jqueryui.com/progressbar/#animated






简单实用后台任务执行框架(Struts2+Spring+AJAX前端web界面可以获取进度)的更多相关文章

  1. Eclipse搭建SSH框架(Struts2+Spring+Hibernate)

    见识少的我经过一天多的研究才知道,在MyEclipse中搭好的框架的配置文件和jar包是通用的.接下来——亮剑! 工具:Eclipse+Tomcat+Mysql 一.先在Eclipse中配置好Tomc ...

  2. 用MyEclipse搭建SSH框架(Struts2 Spring Hibernate)

    1.new一个web project. 2.右键项目,为项目添加Struts支持. 点击Finish.src目录下多了struts.xml配置文件. 3.使用MyEclipse DataBase Ex ...

  3. 使用CXF框架集成Spring实现SOAP Web Service

  4. 深入学习微框架:Spring Boot(转)

    转:http://www.infoq.com/cn/articles/microframeworks1-spring-boot/ 相关参考: https://spring.io/guides/gs/s ...

  5. 深入学习微框架:Spring Boot - NO

    http://blog.csdn.net/hengyunabc/article/details/50120001 Our primary goals are: Provide a radically ...

  6. 深入学习微框架:Spring Boot

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过 ...

  7. 自己实现的简单MVC框架(类似Struts2+Spring)

    一.框架简介 本框架是一个类似于Struts2+Spring的框架,目的在于个人钻研和技术分享,将流行技术框架Struts2.Spring中使用到的主要技术以较为简化的方式实现出来,给大家一个更直观的 ...

  8. SSH(Struts2+Spring+Hibernate)框架搭建流程

    添加支持 我先介绍的是MyEclipse9的自带框架支持搭建过程:(完全的步骤 傻瓜式的学习..~) 首先我们来搭建一个Web项目: 一.Hibernate(数据层)的搭建: 相关描述 Ⅰ.服务器与数 ...

  9. Struts2+Spring+Hibernate 三大框架的合并集成

    这次来看看Struts2+Spring+Hibernate三大框架的整合应用,主要是Spring和Hibernate框架的整合,因为前边已经将Strtus2+Spring整合过了基本一样.  首先看一 ...

随机推荐

  1. poj 2229 Ultra-QuickSort(树状数组求逆序数)

    题目链接:http://poj.org/problem?id=2299 题目大意:给定n个数,要求这些数构成的逆序对的个数. 可以采用归并排序,也可以使用树状数组 可以把数一个个插入到树状数组中, 每 ...

  2. 用Nginx实现Session共享的均衡负载

    前言 大学三年多,也做个几个网站和APP后端,老是被人问到,如果用户多了服务器会不会挂,总是很尴尬的回答:“哈哈,我们的用户还少,到了服务器撑不住的时候,估计都上市了吧”.说是这么说,但是对于有强迫症 ...

  3. HDU 1402 A * B Problem Plus (FFT模板题)

    FFT模板题,求A*B. 用次FFT模板需要注意的是,N应为2的幂次,不然二进制平摊反转置换会出现死循环. 取出结果值时注意精度,要加上eps才能A. #include <cstdio> ...

  4. PyCharm 4.0下载(附keygen)

    百度网盘:http://pan.baidu.com/s/1nvAdEM9  密码:xfz9

  5. HTML精确定位:scrollLeft,scrollWidth,clientWidth,offsetWidth之全然具体解释

      HTML:scrollLeft,scrollWidth,clientWidth,offsetWidth究竟指的哪到哪的距离之全然具体解释scrollHeight: 获取对象的滚动高度. scrol ...

  6. 选择Android还是IOS开发?

    选择Android还是IOS? 随着移动互联网的如日中天,涌现了越来越多的开发者.IOS优秀的用户体验,Android极高的用户群,这对于开发者来说陷入了选择困难的境地,尤其是新入门的开发者,精力有限 ...

  7. 【C疯狂的教材】(九)C语言指针(一)

    1.什么是地址? 内存是由若干个1个字节的内存单元组成的存储器 计算机为了管理内存单元.给每一个字节都进行编号 编号的就是地址 2.什么是指针? 地址就是指针 地址: 地址指向的内存单元值:编号相应的 ...

  8. Linux是如何启动的

    今天早上在上操作系统课的时候,老师有提到计算机从按下开关键到最后由操作系统全然接管的整个过程. 只是讲课毕竟是十分抽象的,由于之前自己也看过这方面的内容,可是老是记不住,所以今天晚上就花了点时间,把& ...

  9. CodeSmith使用总结--调用自定义方法

    上一篇读取了一个表的内容,但是到了真正应用的时候还是不够用的,我们很容易可以对比出来,SQL里边的数据类型的定义和C#中有所不同,比如Saler--String,大写的String在C#中不是一个类型 ...

  10. DEV GridControl 获取选中行的数据

    private void gridView1_FocusedRowChanged(object sender, DevExpress.XtraGrid.Views.Base.FocusedRowCha ...