1. 需求背景和工具简介
1.1 需求背景
最近因为公司需要管理上传的附件,准备把过期的文件夹(我们都是在指定目录下以日期8位来规定文件夹命名)移走进行备份,这时候就需要一个定时任务来进行定时检查文件夹的日期是否达到过期标准。针对这样的需求,我就想到用Java实现一个定时任务程序,然后做成windows的服务让其自动运行。关于程序,我花了点时间写了出来并且成功满足了需求,而关于部署成服务,我在网络上找了一些方案,最终决定使用Java Service Wrapper工具来实现。但在实现过程中发现,虽然网络上有很多教程,但最后发现都是不对或者不完整的, 导致花了很多时间也没能实现。最终我还是根据官方的使用介绍一步一步实验踩坑,才部署成功!所以,我打算把实现的过程记录下来,帮助需要的朋友!
1.2 工具简介
Java Service Wrapper(下面简称wrapper)是目前较为流行的将Java程序部署成Windows服务的解决方案(官方地址:http://wrapper.tanukisoftware.com/doc/english/download.jsp)据我了解还有Apache Common Daemon也是很不错的,Tomcat就是利用该工具实现的,有兴趣的朋友可以尝试一下。wrapper使用方面比daemon较为简单一点,对于windows版本,只有32位有社区版本,其他是收费的,该版本也足够我们使用了, 本文将讨论如何使用wrapper把我们的程序打包成WIN服务!
1.3 开发工具
我目前使用的是Eclipse进行开发,当然你可以使用任何其他开发工具,最终我们也只是需要编译后的文件而已!
2. 使用步骤详述
2.1 第一步:下载wrapper工具包
请至官网下载wrapper的工具包http://wrapper.tanukisoftware.com/doc/english/download.jsp,选择下图中标识的版本进行下载,其他windows版本是收费的
下载的文件是压缩包,先解压缩后备用,解压缩后的文件目录结构应该如下图所示:
2.2 第二步:准备我们的程序(如对本程序无兴趣,可直接拷贝或下载源码后阅读2.3)
这里,我就以我这个文件自动管理服务为例,来讲解如何将程序打包成服务,因为本项目正好有使用第三方jar包,可以直观的看出如何处理第三方Jar包。该程序使用了Quartz定时任务库来实现定时任务。首先,我的工程目录结构是这样的:
引用到的第三方jar包是如下这些:
关于Quartz,可去官网下载Jar包:http://www.quartz-scheduler.org/,对于Quartzde使用的具体讲解可以搜索其他博主的博客
我会把我这个程序的代码贴出来给有需要的朋友,这会使博文有点长甚至有点跑题,还请见谅
当然你可以建立同样的工程,相同的目录,然后建立相同的java文件,并拷贝这些代码进行测试
log4j.properties文件我就不多说了,这个文件可以直接拷贝,只需修改log4j.appender.D.File和log4j.appender.E.File这两个位置的路径即可,不用多做解释
### \u8BBE\u7F6E###
log4j.rootLogger = debug,stdout,D,E
### \u8F93\u51FA\u4FE1\u606F\u5230\u63A7\u5236\u62AC ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### \u8F93\u51FADEBUG \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### \u8F93\u51FAERROR \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u523
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
下面我们来实现能够扫描文件夹同时进行处理的类,我使用xml文件配置的方式,能够灵活配置需要扫描文件夹位置、处理方式以及目标文件夹位置:
来看一下src目录下面的fileHandleJobConfig.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 配置需要检查的文件目录,全部目录都配置到日期文件夹的上一层 -->
<root>
<!-- 配置希望处理的时间间隔,单位是月,即希望将与当前日期相差几个月的文件夹进行处理 -->
<time_interval>
<value>3</value>
</time_interval>
<!-- 过期文件目录根节点,每一个直接子节点代表一种文件目录,可配置多个 -->
<old_file_path>
<!-- 子节点 -->
<file_path_1>
<!-- 过期文件目录 -->
<file_path>E:/TDDOWNLOAD/testdir</file_path>
<!-- 标记:希望程序对旧的文件实行的处理是什么,move表示复制、delete表示直接删除,md表示剪切,如果为空,则默认是move -->
<operate>md</operate>
<!-- 如果处理标记为:move,则该节点表示希望移动到哪个位置,必须提供该节点值 -->
<new_file_path>E:/TDDOWNLOAD/newtestdir</new_file_path>
</file_path_1>
</old_file_path>
</root>
静态常量的定义 : StaticFieldValue.java,用来比较配置的操作类型:move delete md
package com.op.quartz.job;
/**
* 一些静态常量的定义
*
* @author Ivy
*
*/
public class StaticFieldValue {
// 表示操作类型的移动后不删除
public static final String OPERATE_TYPE_MOVE = "MOVE";
// 表示操作类型的删除
public static final String OPERATE_TYPE_DELETE = "DELETE";
// 表示操作类型的移动后删除
public static final String OPERATE_TYPE_MD = "MD";
}
其次,实现文件夹扫描类,能够读取上面的配置文件并进行相应处理 : FileHandler.java
package com.op.quartz.job;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
*
* 过期文件处理工具类
*
* 逻辑:根据fileHandleJobConfig.xml文件里面的配置进行处理
*
* @author Johnny.Ji
*
*/
public class FileHandler {
// 设置Log4j,每一步的操作必须有日志
private static Logger log = Logger.getLogger(FileHandler.class);
// 线程安全的缓存池
private static ThreadLocal<FileHandler> threadLocal = new ThreadLocal<>();
// 单例模式的对象构建器
public static FileHandler getInstance() {
FileHandler fileHandler = threadLocal.get();
if (fileHandler == null) {
fileHandler = new FileHandler();
threadLocal.set(fileHandler);
}
return fileHandler;
}
/**
* 对外提供的用来启动程序的统一的入口
*
*
*/
public void handle() {
try {
readXML();
} catch (DocumentException e) {