利用JAVA Service Wrapper把JAVA程序做成windows服务
今天做了一个读取数据入库的程序。由于读取的数据每天都更新,于是就想把程序做成一个服务,每天定时执行。研究了一下,发现有几种方式可以做。下面我主要记录一下JAVA Service Wrapper方式。
一、下面是整个程序的功能部分:
1.连接数据库的功能。我写了一个DBConnecter类,是一个单例。
public class DBConnecter { private static DBConnecter instance = null; private static Connection conn = null; private DBConnecter() { } public synchronized static DBConnecter getInstance() { if (instance == null) { instance = new DBConnecter(); } return instance; } //连接数据库 public Connection getConnection(String driver, String url) { try { Class.forName(driver); conn = DriverManager.getConnection(url); } catch (Exception e) { System.out.println(e.getMessage()); } return conn; } //Close关闭数据库连接 public void Close() { try { conn.close(); } catch (Exception e) { } } }
2.然后是针对数据库的一个操作类DataWriter。
public class DataWriter { public void writeData(DBConnecter dbcon, List<Map<String, Object>> maps, String driver, String url) { if (maps == null || maps.isEmpty()) { System.out.println("当前没有可写入数据,请等待..."); return; } Connection con = dbcon.getConnection(driver, url); if (con != null) { try { long datatime = new Date().getTime() / 1000; System.out.println("写数据开始,当前时间为[" + datatime + "]!"); PreparedStatement pst_insert = con.prepareStatement(Parameter.sql_insert); PreparedStatement pst_minsert = con.prepareStatement(Parameter.sql_minsert); PreparedStatement pst_update = con.prepareStatement(Parameter.sql_update); for (Map<String, Object> map : maps) { // for (String name : Parameter.pName) { // System.out.println(name + ":" + map.get(name)); // } if (ifHaveRecord(con, map, driver, url)) { updateData(pst_update, map, datatime); insertData(pst_minsert, map, datatime); } else { insertData(pst_insert, map, datatime); } } pst_insert.executeBatch(); pst_minsert.executeBatch(); pst_update.executeBatch(); con.commit(); dbcon.Close(); long time = new Date().getTime() / 1000; System.out.println("写数据结束,耗时[" + (time - datatime) + "秒]!"); } catch (SQLException ex) { try { con.rollback(); } catch (SQLException ex1) { Logger.getLogger(DataWriter.class.getName()).log(Level.SEVERE, null, ex1); } } finally { dbcon.Close(); } } } public void insertData(PreparedStatement pst_insert, Map<String, Object> map, long datatime) { try { pst_insert.setLong(1, Long.parseLong(map.get(Parameter.pName[0]).toString())); pst_insert.setLong(2, datatime); pst_insert.setLong(3, dateFormat(map.get(Parameter.pName[1]).toString(), Parameter.FORMAT_STR).getTime() / 1000); pst_insert.setObject(4, map.get(Parameter.pName[2])); pst_insert.setObject(5, map.get(Parameter.pName[3])); pst_insert.setObject(6, map.get(Parameter.pName[4])); pst_insert.setObject(7, map.get(Parameter.pName[5])); pst_insert.setObject(8, map.get(Parameter.pName[6])); pst_insert.setObject(9, map.get(Parameter.pName[7])); pst_insert.executeUpdate(); // System.out.println(pst_insert.toString() + "_insert"); } catch (SQLException ex) { Logger.getLogger(DataWriter.class.getName()).log(Level.SEVERE, null, ex); } } public void updateData(PreparedStatement pst_update, Map<String, Object> map, long datatime) { try { pst_update.setLong(7, Long.parseLong(map.get(Parameter.pName[0]).toString())); pst_update.setLong(8, dateFormat(map.get(Parameter.pName[1]).toString(), Parameter.FORMAT_STR).getTime() / 1000); pst_update.setObject(9, map.get(Parameter.pName[2])); pst_update.setLong(1, datatime); pst_update.setObject(2, map.get(Parameter.pName[3])); pst_update.setObject(3, map.get(Parameter.pName[4])); pst_update.setObject(4, map.get(Parameter.pName[5])); pst_update.setObject(5, map.get(Parameter.pName[6])); pst_update.setObject(6, map.get(Parameter.pName[7])); pst_update.executeUpdate(); // System.out.println(pst_update.toString() + "_update"); } catch (SQLException ex) { Logger.getLogger(DataWriter.class.getName()).log(Level.SEVERE, null, ex); } } public boolean ifHaveRecord(Connection con, Map<String, Object> map, String driver, String url) { if (con != null) { try { PreparedStatement pst = (PreparedStatement) con.prepareStatement(Parameter.sql_select); pst.setLong(1, Long.parseLong(map.get(Parameter.pName[0]).toString())); pst.setLong(2, dateFormat(map.get(Parameter.pName[1]).toString(), Parameter.FORMAT_STR).getTime() / 1000); pst.setObject(3, map.get(Parameter.pName[2])); ResultSet rs = pst.executeQuery(); if (rs != null) { return true; } } catch (SQLException e) { Logger.getLogger(DataWriter.class.getName()).log(Level.SEVERE, null, e); } } return false; } public static Date dateFormat(String str, String format_str) { DateFormat format = new SimpleDateFormat(format_str); try { return format.parse(str); } catch (ParseException ex) { Logger.getLogger(DataWriter.class.getName()).log(Level.SEVERE, null, ex); } return null; } }
3.由于我所有的属性值都是通过配置文件得到的,下面是配置文件的读入类ConfigerReader,这也是一个单例。
public final class ConfigerReader { private static ConfigerReader instance = null; private static Map<String, Object> configerMap = null; private ConfigerReader() { } public synchronized static ConfigerReader getInstance() { if (instance == null) { instance = new ConfigerReader(); } return instance; } public Map<String, Object> getConfigerReader(String configerPath) { if (configerMap != null) { return configerMap; } InputStream inputStream = null; Properties p = new Properties(); configerMap = new HashMap<String, Object>(); try { inputStream = new FileInputStream(configerPath); p.load(inputStream); configerMap = new HashMap<String, Object>(); configerMap.put(Parameter.WFID, p.getProperty(Parameter.WFID)); configerMap.put(Parameter.STATIONID, p.getProperty(Parameter.STATIONID)); configerMap.put(Parameter.WFNAME, p.getProperty(Parameter.WFNAME)); configerMap.put(Parameter.WFTIME1, p.getProperty(Parameter.WFTIME1)); configerMap.put(Parameter.WFTIME2, p.getProperty(Parameter.WFTIME2)); configerMap.put(Parameter.DRIVER, p.getProperty(Parameter.DRIVER)); configerMap.put(Parameter.URL, p.getProperty(Parameter.URL)); configerMap.put(Parameter.USER, p.getProperty(Parameter.USER)); configerMap.put(Parameter.PWD, p.getProperty(Parameter.PWD)); configerMap.put(Parameter.DB, p.getProperty(Parameter.DB)); configerMap.put(Parameter.LOCALDIR, p.getProperty(Parameter.LOCALDIR)); configerMap.put(Parameter.BACKDIR, p.getProperty(Parameter.BACKDIR)); } catch (IOException ex) { Logger.getLogger(ConfigerReader.class.getName()).log(Level.SEVERE, null, ex); } finally { try { inputStream.close(); } catch (IOException ex) { Logger.getLogger(ConfigerReader.class.getName()).log(Level.SEVERE, null, ex); } } return configerMap; } }
4.下面是我的WPD文件读写类DataReader。
public class DataReader { /** * 以行为单位读取文件,常用于读面向行的格式化文件 */ public List<Map<String, Object>> readFileByLines(String readFilePath, String backFilePath, int wfid, int stationId) { File file = new File(readFilePath); File[] flist = file.listFiles(); BufferedReader reader = null; List<Map<String, Object>> data = new ArrayList<Map<String, Object>>(); try { // System.out.println("以行为单位读取文件内容,一次读一整行:"); if (flist == null || flist.length == 0) { System.out.println("当前没有数据文件!"); return null; } else { System.out.println("发现" + flist.length + "个文件!"); } for (int i = 0; i < flist.length; i++) { String name = flist[i].getName(); String currentfile = readFilePath + File.separator + name; // int namelength = name.length(); // String fileDate = name.substring(namelength - 12, namelength - 4); // System.out.println("文件的时间为:" + fileDate); System.out.println("读取第" + (i + 1) + "个文件,文件名为[" + name + "]"); reader = new BufferedReader(new FileReader(currentfile)); // 一次读入一行,直到读入null为文件结束 int daytype = 1; int stempid = 0; // String time = fileDate.substring(0, 4) + "-" + fileDate.substring(4, 6) + "-" + fileDate.substring(6); String time = ""; String tempString; //按行来读取文件内容 while ((tempString = reader.readLine()) != null) { Map<String, Object> map = new HashMap<String, Object>(); //判断是否含有[000#]类型的内容 Pattern stapattern = Pattern.compile("[0-9]{3}\\#"); Matcher stamatcher = stapattern.matcher(tempString); if (stamatcher.find()) {//如果包含取出#号前面的数字,若找到说明为新的station数据,重置daytype和time stempid = Integer.parseInt(stamatcher.group().split("#")[0]); // System.out.println("stempid " + ": " + stempid); daytype = 1; time = ""; } if (stempid != stationId && stationId != 0) {//若当前的sId与stationid不符,说明当前的station数据不符合要求,跳出循环 continue; } //判断是否含有[0000-00-00 00:00:00]时间类型的内容 Pattern timepattern = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}"); Matcher timematcher = timepattern.matcher(tempString); //如果能查询到时间内容 if (timematcher.find()) { //获取时间的日期属性[0000-00-00] String date = timematcher.group().substring(0, 10); //如果daytype>1的话,就添加daytype到map中 if (daytype > 1) { //用正则表达式获取小数值 Pattern numpattern = Pattern.compile("-?[0-9]+\\.[0-9]{2}"); Matcher nummatcher = numpattern.matcher(tempString); //从pName第三个开始向map中存入值 int pKey = 3; while (nummatcher.find()) { map.put(Parameter.pName[pKey], nummatcher.group()); pKey++; } //如果不能找到 if (pKey > 3) { //添加wid到map中 map.put(Parameter.pName[0], wfid); //把nwpdatetime添加到map中 map.put(Parameter.pName[1], timematcher.group()); //添加daytype到map中 map.put(Parameter.pName[2], daytype); data.add(map); } } //如果当前日期与上一次日期不一致,daytype加1,并给time赋值 if (!time.equals(date)) { time = date; daytype++; } } } File backFileDir = new File(backFilePath + File.separator + name); if (!backFileDir.exists()) { file.mkdir(); } copyFile(new File(currentfile), backFileDir); reader.close(); deleteFile(currentfile); } } catch (IOException e) { Logger.getLogger(DataReader.class.getName()).log(Level.SEVERE, null, e); } finally { if (reader != null) { try { reader.close(); } catch (IOException ex) { Logger.getLogger(DataReader.class.getName()).log(Level.SEVERE, null, ex); } } } // System.out.println("数据长度:" + data.size()); return data; } //复制文件 public void copyFile(File sourceFile, File targetFile) { System.out.println("复制文件[" + sourceFile.getName() + "]到[" + targetFile.getPath() + "]开始!"); BufferedInputStream inBuff = null; BufferedOutputStream outBuff = null; try { // 新建文件输入流并对它进行缓冲 inBuff = new BufferedInputStream(new FileInputStream(sourceFile)); // 新建文件输出流并对它进行缓冲 outBuff = new BufferedOutputStream(new FileOutputStream(targetFile)); // 缓冲数组 byte[] b = new byte[1024 * 5]; int len; while ((len = inBuff.read(b)) != -1) { outBuff.write(b, 0, len); } // 刷新此缓冲的输出流 outBuff.flush(); } catch (IOException ex) { Logger.getLogger(DataReader.class.getName()).log(Level.SEVERE, null, ex); } finally { try { if (inBuff != null) { inBuff.close();// 关闭流 } if (outBuff != null) { outBuff.close();// 关闭流 } } catch (IOException ex) { Logger.getLogger(DataReader.class.getName()).log(Level.SEVERE, null, ex); } } } //删除文件 public void deleteFile(String filepath) { File file = new File(filepath);// 定义文件路径 // 路径为文件且不为空则进行删除 if (file.isFile() && file.exists()) { System.out.println(file.isFile()); if (file.delete()) { System.out.println("删除文件[" + filepath + "]成功!"); } else { System.out.println("删除文件[" + filepath + "]失败!"); } } } }
我需要根据配置文件的要求获取相应station下面的数据,而且获取的数据入库要求是从00:15:00开始,到下一天00:00:00算一个天。所以在判断的时候,是先放入缓存MAP中,后比较时间标识。
除此,在删除数据文件的时候,一定要先关掉流,再执行删除操作,负责会失败。
5,接下来是定时执行程序ReaderTimerTask,他继承了TimerTask类,在这个类中,获取了配置参数,然后调用了DataReader解析WPD数据,调用DataWriter写入数据库。
public class ReaderTimerTask extends TimerTask { @Override public void run() { Map map = ConfigerReader.getInstance().getConfigerReader(Parameter.configer); String wfId = map.get(Parameter.WFID).toString().trim(); if (!"".equals(wfId.toString())) { System.out.println("获取电场ID成功!"); } else { System.out.println("获取电场ID失败!"); return; } String stationId = map.get(Parameter.STATIONID).toString().trim(); if (!"".equals(stationId.toString())) { System.out.println("获取气象站ID成功!"); } else { System.out.println("获取气象站ID失败!"); return; } String localDir = map.get(Parameter.LOCALDIR).toString().trim(); if (!"".equals(localDir)) { System.out.println("获取本地文件目录成功!"); } else { System.out.println("获取本地文件目录失败!"); return; } String backDir = map.get(Parameter.BACKDIR).toString().trim(); if (!"".equals(backDir)) { System.out.println("获取文件备份目录成功!"); } else { System.out.println("获取文件备份目录失败!"); return; } String driver = map.get(Parameter.DRIVER).toString().trim(); if (!"".equals(driver)) { System.out.println("获取数据库驱动成功!"); } else { System.out.println("获取数据库驱动失败!"); return; } String url = map.get(Parameter.URL).toString().trim(); if (!"".equals(localDir)) { System.out.println("获取数据库URL成功!"); } else { System.out.println("获取数据库URL失败!"); return; } DBConnecter dbcon = DBConnecter.getInstance(); if (dbcon != null) { System.out.println("数据库连接成功!"); } else { System.out.println("数据库连接失败!"); return; } DataWriter writer = new DataWriter(); DataReader reader = new DataReader(); List<Map<String, Object>> dataMaps = reader.readFileByLines(localDir, backDir, Integer.parseInt(wfId), Integer.parseInt(stationId)); writer.writeData(dbcon, dataMaps, driver, url); } }
6,最后就是主函数类WPDAnalysis。这个类也获取了读取参数类。并根据读取的参数设置执行时间。
public class WPDAnalysis { /** * @param args the command line arguments */ public static void main(String[] args) { Map map = ConfigerReader.getInstance().getConfigerReader(Parameter.configer); String wftime1 = map.get(Parameter.WFTIME1).toString(); String wftime2 = map.get(Parameter.WFTIME2).toString(); Timer timer = new Timer(); //设置执行时间 Calendar calendar = Calendar.getInstance(); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); int day = calendar.get(Calendar.DAY_OF_MONTH);//每天 int hour = 9; int minute = 00; int second = 00; //定制每天的09:00:00执行, if (!"".equals(wftime1.trim())) { int time = Integer.parseInt(wftime1); hour = time / 3600; minute = time % 3600 / 60; second = time % 3600 % 60; } calendar.set(year, month, day, hour, minute, second); Date date = calendar.getTime(); System.out.println("服务第一次执行开始,当前时间为" + date.toString()); timer.schedule(new ReaderTimerTask(), date, Parameter.READERRATE); if (!"".equals(wftime2.trim())) { int time = Integer.parseInt(wftime2); hour = time / 3600; minute = time % 3600 / 60; second = time % 3600 % 60; calendar.set(year, month, day, hour, minute, second); System.out.println("服务第二次执行开始,当前时间为" + date.toString()); date = calendar.getTime(); timer.schedule(new ReaderTimerTask(), date, Parameter.READERRATE); } } }
除了这些主要功能类,还有一个参数配置类Parameter。程序中所有的参数都能在这个类中找到。
public final class Parameter { //主配置文件 public final static String configer = "D:\\Analysis\\configer.properties"; //数据库属性 public final static String WFID = "WFID"; public final static String STATIONID = "STATIONID"; public final static String WFNAME = "WFNAME"; public final static String WFTIME1 = "WFTIME1"; public final static String WFTIME2 = "WFTIME2"; public final static String DRIVER = "DRIVER"; public final static String URL = "URL"; public final static String USER = "USER"; public final static String PWD = "PWD"; public final static String DB = "DB"; //文件操作属性 public final static String LOCALDIR = "LOCALDIR"; public final static String BACKDIR = "BACKDIR"; public final static int READERRATE = 10 * 1000; //文本属性 public final static String[] pName = {"wfid", "nwpdate", "daytype", "uavg", "vavg", "temperature", "pressure", "humidity", "vspeed"}; public final static String FORMAT_STR = "yyyy-MM-dd HH:mm:ss"; //SQL插入语句 public final static String sql_insert = "insert into nwpdata(wfid,datatime,nwptime,daytype,temperature,pressure,humidity) " + "values (?,?,?,?,?,?,?)"; //SQL备份插入语句 public final static String sql_minsert = "insert into nwpdatam(wfid,datatime,nwptime,daytype,temperature,pressure,humidity) " + "values (?,?,?,?,?,?,?)"; public final static String sql_update = "update nwpdata set datatime = ?,uavg4 = ?, vavg4 = ?," + "temperature = ?,pressure = ?,humidity = ? " + "where wfid = ? and nwptime = ? and daytype = ?"; public final static String sql_select = "select * from nwpdata where wfid = ? and nwptime = ? and daytype = ?"; }
程序的配置文件类如下:
[SERVICE]
WFTIME1=28800
WFTIME2=
WFID=1
STATIONID=54
[DATABASE]
TYPE=sql_server
DRIVER=com.microsoft.sqlserver.jdbc.SQLServerDriver
URL=jdbc:sqlserver://192.168.10.104;user=sa;password=cast1234;database=DB
USER=sa
PWD=cast1234
DB=DB
[NRFMNWP]
LOCALDIR=D:\\Analysis\\Output
BACKDIR=D:\\Analysis\\Back
二、整个项目需要打包生成WPDAnalysis.jar,然后我们就可以做我们的window服务了。
第一步在http://wrapper.tanukisoftware.com/doc/english/download.jsp下载我们的java service wrapper工具,下载后解压。工具分32位跟64位,看自己所需。
第二步构建你的程序工作目录,比如我构建D:\WPDAnalysis_64。然后在目录下建造lib,logs,bin,conf等文件夹。其实目录名字也可以自己随便定义,只要最后配置正确即可。同时把你的WPDAnalysis.jar也放在D:\WPDAnalysis_64下面。
第三步把解压后的文件夹中src\bin中的文件复制到新建的bin文件夹下面。并把所有文件的in后缀去掉。同时把解压后文件夹中bin下的wrapper.exe也放到新建的bin下。
第四步把解压后的文件夹中src\conf中的文件复制到新建的conf文件夹中。把in后缀去掉。
第五步把解压后的文件夹中lib中的wrapper.jar与wrapper.dll放大新建的lib下面。同时把WPDAnalysis程序所需要的第三方jar包夜放在这里。我这里使用sqljdbc4.jar。
第六步配置wrapper.conf文件,主要配置选项如下:
#Java的位置
wrapper.java.command=D:\Java\jdk1.6.0_31\bin\java
#如果你系统已经配置了%JAVA_HOME%的环境变量,上面的就可以不配置了。如下:
#wrapper.java.command=%JAVA_HOME%/bin/java
#classpath:
wrapper.java.classpath.1=../WPDAnalysis.jar
wrapper.java.classpath.2=../lib/wrapper.jar
wrapper.java.classpath.3=../lib/sqljdbc4.jar
# Java Library Path (取决于Wrapper.DLL 或者libwrapper.so)
wrapper.java.library.path.1=../lib
#MAIN CLASS 此处决定了使用Java Service Wrapper的方式
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
#你的Java应用类,我这里是这样子
wrapper.app.parameter.1=wpdanalysis.WPDAnalysis
#服务名称
wrapper.name=WPDAnalysis_64
#服务部署名称,会显示到window服务中的名称栏
wrapper.displayname=Analysis WPD Data Into DataBase For X64
#服务的描述
wrapper.description=Analysis WPD Data Into DataBase For X64
配置以后,点击bin文件夹下面的App.bat进行测试,如果能够在console中出现正常结果的话就表明配置正确。然后点击InstallApp-NT.bat安装服务,也可以点击UninstallApp-NT.bat卸载服务。成功安装服务后可以在window服务管理中看到哦。
以上就是java序做成服务的主要步骤。
作者:EliteQing
出处:http://www.cnblogs.com/liinux/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
欢迎加入网络爬虫QQ群:322937592 ;数据分析&网络爬虫
网络爬虫模拟登录开源项目ghost-login:ghost-login
微信订阅号:网络爬虫AI数据分析【WebCrawlerAIDA】