利用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序做成服務的主要步驟。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM