package com.suteam.zsyun.job;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.log4j.Logger;
import com.alibaba.fastjson.JSON;
import com.suteam.zsyun.application.model.ApplicationFilePack;
import com.suteam.zsyun.commonUtil.FileUtil;
import com.suteam.zsyun.commonUtil.HttpAuthUtil;
import com.suteam.zsyun.commonUtil.ModifyConfig;
import com.suteam.zsyun.config.Config;
/**
* agent守护进程项目定时任务
*
* @author zsyun_gsa
*
*/
public class BasicJobUtil {
public static Logger log = Logger.getLogger(BasicJobUtil.class);
public static void executeTask() {
log.info(" *****agent守护进程定时任务开始***** ");
try {
// 获取jar文件当前路径
String path = System.getProperty("user.dir");
log.info("jar文件当前路径:"+path);
// 获取配置文件路径
String configPath = FileUtil.getConfigPath();
// 读取配置文件config.ini,获取版本号version_Number与当前jar文件名称
Properties configProperty = FileUtil.getConfig(configPath + Config.configName);
String versionNumber = configProperty.getProperty("version_Number");
String agentName = configProperty.getProperty("agent_Name");
// 读取配置文件es.ini,获取code
Properties esProperty = FileUtil.getConfig(configPath + Config.esName);
String code = esProperty.getProperty("code");
// 读取配置文件watch.ini,获取app项目服务器地址
Properties watchProperty = FileUtil.getConfig(configPath + Config.watchName);
String appServer = watchProperty.getProperty("appServer");
// 声明一个存放请求参数的map
Map<String, String> parameterMap = new HashMap<String, String>();
// 拼接调用app项目中的版本查询接口的url和参数
String url = appServer + "/v1/port/appService/query/queryPatch.do";
parameterMap.put("code", code);
parameterMap.put("versionNumber", versionNumber);
// 调用APP项目中版本查询接口
log.info("调用APP项目中版本查询接口:");
String result = HttpAuthUtil.doAuthPost(url, parameterMap);
Map<String, Object> mapResult = (Map<String, Object>) JSON.parse(result);
if (null != mapResult && "ok".equals(mapResult.get("result"))) {
log.info("调用APP项目中版本查询接口,ok:");
// 解析获取应用文件包对象
ApplicationFilePack applicationFilePack = JSON.parseObject(String.valueOf(mapResult.get("values")),
ApplicationFilePack.class);
// 判断应用文件包对象是否为空。不为空说明有要更新的agent。
if (null != applicationFilePack) {
log.info("文件包对象不为空,说明有要更新的agent。");
// 获取版本
String version = applicationFilePack.getVersion();
log.info("获取新版本version:" + version);
if (!version.equals(versionNumber)) {
log.info("新旧版本不同。");
// 获取应用文件路径
String filePath = applicationFilePack.getFilePath();
log.info("新文件路径:"+filePath);
// 获取文件hash
String hash = applicationFilePack.getHash();
log.info("新文件hash:"+hash);
// 获取名称,如agent_1.2.jar
String name = applicationFilePack.getName();
log.info("新文件名称:" + name);
// 调用下载补丁方法
String getHash = downLoadFromUrl(filePath, name, path);
if (!getHash.equals(hash)) {
// 将下载失败的文件删除
if (path.contains("/")) {
path += "/";
}
if (path.contains("\\")) {
path += "\\";
}
log.info("将下载失败的文件删除,要删除的文件为:"+path + name);
deleteFile(path + name);
// hash不同,下次定时任务重新下载
log.info("hash不同,下次定时任务重新下载");
} else {
log.info("新下载的jar文件与原文件hash相同。");
// 修改配置文件,将文件版本更新成新版本
log.info("修改配置文件更新版本信息:");
String[] keyArray = new String[2];
keyArray[0] = "version_Number";
keyArray[1] = "agent_Name";
String[] valueArray = new String[2];
valueArray[0] = version;
valueArray[1] = name;
log.info("新的版本为:" + version);
String fileName = "config.ini";
ModifyConfig.modifyConfig(keyArray, valueArray, fileName);
log.info("修改配置文件更新版本信息,完成。");
log.info("将原agent停止:");
if (path.contains("/")) {
path += "/";
log.info("Linux将原agent停止:");
String psCommander = "ps -ef|grep "+agentName;
log.info("查看进程指令:" + psCommander);
Process ps = Runtime.getRuntime().exec(new String[] { "sh", "-c", psCommander });
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
String ret = sb.toString();
// 将获取结果去除回车拆分
String[] resultStr = ret.split("\n");
for (int j = 0; j < resultStr.length; j++) {
String s = resultStr[j];
// 去除tab键
s = s.replace(" ", " ");
log.info("去除tab键:" + s);
// 将所有的多空格转成单空格
s = s.replaceAll(" +", " ");
log.info("将所有的多空格转成单空格:" + s);
if (s.startsWith(" ")) {
// 将第一个空格截取去除
s = s.substring(1, s.length());
log.info("将第一个空格截取去除:" + s);
}
// 将数据以空格拆分:root 16608 4888 0 15:08 pts/1
// 00:00:47 java -jar agent.jar
String[] retStr = s.split(" ");
log.info("将数据以空格拆分:" + s);
// 获取进程结果的最后一个数据(所下达的指令为何)
String cmdCommander = "";
for (int i = 7; i < retStr.length; i++) {
cmdCommander = cmdCommander + retStr[i];
}
log.info("logCode进程结果的最后一个数据:" + cmdCommander);
log.info("pid为:" + retStr[1]);
if (cmdCommander.contains("java-jar")) {
// 包含文件名,说明这个进程为要杀死的agent进程。获取进程的pid(第二个数据)。
String pid = retStr[1];
log.info("pid为:" + retStr[1]);
// 获取cp进程的标识号,拼接杀进程指令kill -9 进程标识号
String killCommander = "kill -9 " + pid;
// 执行杀进程指令
log.info("---开始执行kill指令:" + killCommander);
Runtime.getRuntime().exec(killCommander);
log.info("---执行kill指令完毕。");
}
}
// 文件路径
String shfileName = path+"/restarJar.sh";
log.info("获取sh文件路径如下 :"+shfileName);
// 1.删除文件
deleteFile(shfileName);
log.info("删除文件完成。");
// 2.创建文件
createNewFile(shfileName);
log.info("创建文件完成。");
//changeParam为要追加到.sh文件的信息
String changeParam = "nohup java -jar /usr/agenthost/" + name +" > /usr/agenthost/logs/myout.file 2>&1 &";
log.info("追加到.sh文件的信息:"+ changeParam);
// 3.追加内容 \n代表回车换行
StringBuffer strBuffer = new StringBuffer();
strBuffer.append("#!/bin/sh\n");
strBuffer.append(changeParam);
appendMethod(shfileName, strBuffer.toString());
log.info("追加完成");
log.info("执行sh文件:bash "+path+"/restarJar.sh");
Runtime.getRuntime().exec("bash "+path+"/restarJar.sh");
log.info("执行sh文件完成");
}
if (path.contains("\\")) {
path += "\\";
log.info("Windows将原agent停止:");
// 查看Windows系统所有正在运行的java程序
String psCommander = "jps -l";
log.info("查看进程指令:" + psCommander);
Process ps = Runtime.getRuntime().exec(psCommander);
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
String ret = sb.toString();
// 将获取结果去除回车拆分
String[] resultStr = ret.split("\n");
for (int j = 0; j < resultStr.length; j++) {
String s = resultStr[j];
// 去除tab键
s = s.replace(" ", " ");
log.info("去除tab键:" + s);
// 将所有的多空格转成单空格
s = s.replaceAll(" +", " ");
log.info("将所有的多空格转成单空格:" + s);
if (s.startsWith(" ")) {
// 将第一个空格截取去除
s = s.substring(1, s.length());
log.info("将第一个空格截取去除:" + s);
}
// 将数据以空格拆分:root 16608 4888 0 15:08 pts/1
// 00:00:47 java -jar agent.jar
String[] retStr = s.split(" ");
log.info("将数据以空格拆分:" + s);
// 获取进程结果的最后一个数据(所下达的指令为何)
if(retStr.length>1){
String cmdCommander = retStr[1];
log.info("logCode进程结果的最后一个数据:" + cmdCommander);
if (cmdCommander.contains(agentName)) {
// 包含文件名,说明这个进程为要杀死的agent进程。获取进程的pid(第二个数据)。
String pid = retStr[0];
log.info("pid为:" + retStr[0]);
// 获取cp进程的标识号,拼接杀进程指令kill -9 进程标识号
String killCommander = "TASKKILL /F /PID " + pid;
// 执行杀进程指令
log.info("---开始执行kill指令:" + killCommander);
Runtime.getRuntime().exec(killCommander);
log.info("---执行kill指令完毕。");
}
}
}
// 重启jar文件路径
String shfileName = path+"\\restarJar.bat";
log.info("获取重启jar文件路径如下 :"+shfileName);
// 删除原文件
deleteFile(shfileName);
log.info("删除原文件完成。");
// 重新创建文件
createNewFile(shfileName);
log.info("重新创建文件完成。");
//changeParam为要追加到.sh文件的信息
String changeParam = "start javaw -jar " + name;
log.info("追加到.bat文件的信息:"+ changeParam);
// 将内容追加到重启bat文件中 \n代表回车换行
StringBuffer strBuffer = new StringBuffer();
strBuffer.append("@echo off\n");
strBuffer.append(changeParam+"\n");
strBuffer.append("success& exit\n");
appendMethod(shfileName, strBuffer.toString());
log.info("追加完成");
log.info("执行重启jarbat文件");
String cmd = "cmd.exe /c start " + shfileName;
Runtime.getRuntime().exec(cmd);
log.info("执行重启jarbat文件完成");
}
}
}
}
} else {
log.info("获取对象为空,没有要更新的!");
}
} catch (Exception e) {
log.info("agent守护进程抛出异常。");
}
log.info(" *****agent守护进程定时任务结束*****");
}
/***
* 对文件做hash处理
*
* @param filename
* @return
* @throws Exception
*/
public static String getMD5Checksum(String filename) throws Exception {
log.info("进入获取hash方法:");
byte[] b = createChecksum(filename);
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);// 加0x100是因为有的b[i]的十六进制只有1位
}
return result;
}
public static byte[] createChecksum(String filename) throws Exception {
InputStream fis = new FileInputStream(filename);// 20px;">将流类型字符串转换为String类型字符串</span>
byte[] buffer = new byte[1024];
MessageDigest complete = MessageDigest.getInstance("MD5"); // 如果想使用SHA-1或SHA-256,则传入SHA-1,SHA-256
int numRead;
do {
numRead = fis.read(buffer); // 从文件读到buffer,最多装满buffer
if (numRead > 0) {
complete.update(buffer, 0, numRead); // 用读到的字节进行MD5的计算,第二个参数是偏移量
}
} while (numRead != -1);
fis.close();
return complete.digest();
}
/**
* 删除文件方法
*/
public static void deleteFile(String fileName) {
File file = new File(fileName);
// 如果是文件,非目录
if (file.isFile()) {
file.delete();
} else {
return;
}
}
/**
* 新建文件方法
*/
public static void createNewFile(String fileName) {
File file = new File(fileName);
// 如果文件不存在,新建一个文件
if (file.isFile() && !file.exists()) {
try {
// 创建文件
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 追加文件方法
*/
public static void appendMethod(String fileName, String content) {
try {
// 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(fileName, true), "GBK");
osw.write(content);
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从网络Url中下载文件
*
* @param urlStr
* @param fileName
* @param savePath
* @throws IOException
*/
public static String downLoadFromUrl(String urlStr, String fileName, String savePath) throws IOException {
String retHash = "";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 设置超时间为3秒
conn.setConnectTimeout(3 * 1000);
// 防止屏蔽程序抓取而返回403错误
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
// 得到输入流
InputStream inputStream = conn.getInputStream();
// 获取自己数组
byte[] getData = readInputStream(inputStream);
// 文件保存位置
File saveDir = new File(savePath);
if (!saveDir.exists()) {
saveDir.mkdir();
}
File file = new File(saveDir + File.separator + fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(getData);
if (fos != null) {
fos.close();
}
if (inputStream != null) {
inputStream.close();
}
log.info("info:" + url + " download success");
// 下载完毕,对补丁(或agent)进行hash处理
try {
if (savePath.contains("/")) {
savePath += "/";
}
if (savePath.contains("\\")) {
savePath += "\\";
}
log.info("hash路径:" + savePath + fileName);
retHash = getMD5Checksum(savePath + fileName);
} catch (Exception e) {
e.printStackTrace();
}
log.info("获取的文件hash:" + retHash);
return retHash;
}
/**
* 从输入流中获取字节数组
*
* @param inputStream
* @return
* @throws IOException
*/
public static byte[] readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
return bos.toByteArray();
}
public static void main(String[] args) {
try {
String hash = getMD5Checksum("H:\\agent_v1.2.jar");
System.out.println(hash);
} catch (Exception e) {
e.printStackTrace();
}
}
}