JavaServiceWrapper使⽤经验总结
1. 需求背景和⼯具简介
1.1 需求背景
最近因为公司需要管理上传的附件,准备把过期的⽂件夹(我们都是在指定⽬录下以⽇期8位来规定⽂件夹命名)移⾛进⾏备份,这时候就需要⼀个定时任务来进⾏定时检查⽂件夹的⽇期是否达到过期标准。针对这样的需求,我就想到⽤Java实现⼀个定时任务程序,然后做成windows的服务让其⾃动运⾏。关于程序,我花了点时间写了出来并且成功满⾜了需求,⽽关于部署成服务,我在⽹络上了⼀些⽅案,最终决定使⽤Java Service Wrapper⼯具来实现。但在实现过程中发现,虽然⽹络上有很多教程,但最后发现都是不对或者不完整的, 导致花了很多时间也没能实现。最终我还是根据官⽅的使⽤介绍⼀步⼀步实验踩坑,才部署成功!所以,我打算把实现的过程记录下来,帮助需要的朋友!
1.2 ⼯具简介
1.3 开发⼯具
我⽬前使⽤的是Eclipse进⾏开发,当然你可以使⽤任何其他开发⼯具,最终我们也只是需要编译后的⽂件⽽已!
2. 使⽤步骤详述
2.1 第⼀步:下载wrapper⼯具包
下载的⽂件是压缩包,先解压缩后备⽤,解压缩后的⽂件⽬录结构应该如下图所⽰:
2.2 第⼆步:准备我们的程序(如对本程序⽆兴趣,可直接拷贝或下载源码后阅读2.3)
这⾥,我就以我这个⽂件⾃动管理服务为例,来讲解如何将程序打包成服务,因为本项⽬正好有使⽤第三⽅jar包,可以直观的看出如何处理
第三⽅Jar包。该程序使⽤了Quartz定时任务库来实现定时任务。⾸先,我的⼯程⽬录结构是这样的:
我会把我这个程序的代码贴出来给有需要的朋友,这会使博⽂有点长甚⾄有点跑题,还请见谅
当然你可以建⽴同样的⼯程,相同的⽬录,然后建⽴相同的java⽂件,并拷贝这些代码进⾏测试
log4j.properties⽂件我就不多说了,这个⽂件可以直接拷贝,只需修改log4j.appender.D.File和log4j.appender.E.File这两个位置的路径即可,不⽤多做解释
下⾯我们来实现能够扫描⽂件夹同时进⾏处理的类,我使⽤xml⽂件配置的⽅式,能够灵活配置需要扫描⽂件夹位置、处理⽅式以及⽬标⽂件夹位置:
### \u8BBE\u7F6E###
### \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
properties是什么文件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.PatternLayoutsubcommittee
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
来看⼀下src⽬录下⾯的l⽂件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 配置需要检查的⽂件⽬录,全部⽬录都配置到⽇期⽂件夹的上⼀层 -->
<root>
<!-- 配置希望处理的时间间隔,单位是⽉,即希望将与当前⽇期相差⼏个⽉的⽂件夹进⾏处理 -->
<time_interval>
<value>3</value>
</time_interval>
gradle使用<!-- 过期⽂件⽬录根节点,每⼀个直接⼦节点代表⼀种⽂件⽬录,可配置多个 -->
<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;
ParseException;
SimpleDateFormat;
import java.util.Calendar;
俄罗斯图95ms轰炸机import java.util.Date;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
*
* 过期⽂件处理⼯具类
*
* 逻辑:根据l⽂件⾥⾯的配置进⾏处理
*
* @author Johnny.Ji
*
*/
public class FileHandler {
// 设置Log4j,每⼀步的操作必须有⽇志
private static Logger log = Logger(FileHandler.class);
// 线程安全的缓存池
private static ThreadLocal<FileHandler> threadLocal = new ThreadLocal<>();
// 单例模式的对象构建器
public static FileHandler getInstance() {
FileHandler fileHandler = ();
if (fileHandler == null) {
fileHandler = new FileHandler();
threadLocal.set(fileHandler);
}
return fileHandler;
}
/**
* 对外提供的⽤来启动程序的统⼀的⼊⼝
*
*
*/
public void handle() {
try {
readXML();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
<(e.getMessage());
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
<(e.getMessage());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
<(e.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
<(e.getMessage());
}
}
/
**
* 读取xml⽂件配置,根据配置进⾏过期⽂件处理
*
* @throws DocumentException
* @throws ParseException
* @throws IOException
shell中awk命令*
*
*/
@SuppressWarnings("unchecked")
private void readXML() throws DocumentException, ParseException, IOException {
/
/ 构建xml⽂件读取器对象
SAXReader saxReader = new SAXReader();
// 读取⽂件,形成⽂档对象
Document document = Class().getResourceAsStream("/l") );
// 获得⽂档根节点
Element rootElement = RootElement();
// 根据根节点获取直接⼦节点元素
Element old_file_path_element = rootElement.element("old_file_path");// <old_file_path>节点
Element time_interval_element = rootElement.element("time_interval");// 时间间隔节点
// 检查⽂档的配置是否正确,如果不正确则进⾏log显⽰
if (time_interval_element == null) {
log.info("配置错误:" + Name() + "节点下未配置<time_interval>节点");
return;
}
if (old_file_path_element == null) {
log.info("配置错误:" + Name() + "节点下未配置<old_file_path>节点");
return;
}
// 取出⽤户希望的时间间隔
Element interval_value_element = time_interval_element.element("value");
if (interval_value_element == null) {
log.info("配置错误:" + time_Name() + "节点下未配置<value>节点");
return;
}
// old_file_path节点下⾯,就是每⼀个⽂件夹的路径,我们需要遍历这个节点下⾯的每⼀个⼦节点
Iterator<Element> iterator = old_file_path_element.elementIterator();
while (iterator.hasNext()) {
Element childElement = ();// 每⼀个⼦节点,这⾥每⼀个⼦节点都是⼀个⽂件地址// 根据取出来的每⼀个节点,再次在此节点上进⾏遍历内部节点
Element filePathElement = childElement.element("file_path");// 取出⽂件路径节点
Element operateElement = childElement.element("operate");// 取出操作类型节点
Element newFilePathElement = childElement.element("new_file_path");// 取出希望移动到新的位置的节点
// 检查⽂档的结构是否符合要求
if (filePathElement == null) {
log.info("配置错误:" + Name() + "节点下未配置<file_path>节点");
return;
}
if (operateElement == null) {
log.info("配置错误:" + Name() + "节点下未配置<operate>节点");
return;
}
if (newFilePathElement == null) {
log.info("配置错误:" + Name() + "节点下未配置<new_file_path>节点");
return;
}
// 如果⽂件路径节点值为空,则放弃此次的操作,继续下⼀次操作
if (Text() == null || Text().equals("")) {
log.info("配置错误:" + Name() + "节点下的<file_path>节点值为空");
} else {
// 如果不为空,则说明是可以进⾏操作的,那么再次取出<operate></operate>节点
// 如果配置了<operate>节点,则检查值是否是move或者md,如果是的话,则<new_file_path>节点值不能为空if (Text() != null && !Text().equals("")
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论