java实现的客户端自更新自重启小程序,程序实现一个client,访问升级服务器配置,根据服务器的配置下载升级包,部署,重启服务。
Update接收启动停止命令控制自身生成和被管理程序
Updatemanager判断是否需要升级,升级的版本比较,升级包的下载和比对,升级包的解压发布和被升级服务的重启任务
Packexecuter 管理需要升级的process,主要是启动停止被管理process
Update作为程序的主入口必须一直在后台运行。被管理process原则上只能通过update程序启动停止,update程序作为管理程序必须要有较高的稳定性,尽量保持功能的简洁,防止崩溃,如果update崩溃,process可能还在后台运行,需要第三方杀死被管理process。或者在重启主程序时做判断当前是否已经有未杀死的process
Update.java
private static final String CMD_EXIT = "/exit";
public static String JarCmd="process startup";//jar包的启动命令 java -jar C\:app.jar
private static Update sUpdate;
public static void main(String[] args) {
String serverIP = PropertiesUtil.getValue("serverIP");
int serverPort = 0;
try {
serverPort = Integer.parseInt(PropertiesUtil.getValue("serverPort", "88"));
} catch (Exception e) {
System.out.println("端口必须是数字");
return;
}
System.out.println("正在启动...");
UpdaterMagager updater = new UpdaterMagager ();
updater.start(serverIP, serverPort);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String cmd = null;
try {
Thread.sleep(3000);
cmd = br.readLine();
} catch (Exception e) {
System.out.println("控制台IO异常");
break;
}
if (cmd == null) {
continue;
}
if (CMD_EXIT.equals(cmd)) {
break;
}
System.out.println("未知的命令");
}
sUpdate.exit();
PackExecutor.exit();
System.out.println("bye");
}
public void start(String ip, int port) {
sUpdate = new UpdaterMagager (ip, port);
sUpdate.start();
}
UpdaterMagager.java
private static final long CHECK_TIME = 5 * 60 * 1000L;//检测时间
private static final String CODE_UPDATE = "1"; // 是否更新标志
private String mServerIP = null;
private int mServerPort = 0;
private boolean mKeepThread = false;
private String savepath=PropertiesUtil.getValue("savepath");
private String unzippath= PropertiesUtil.getValue("unzippath");
private static String versionname;
public UpdaterMagager(String ip, int port) {
mServerIP = ip;
mServerPort = port;
}
public void start() {
mKeepThread = true;
new Thread(this).start();
}
public void exit() {
mKeepThread = false;
}
//通过zip生成md5的值
private String getFileMd5(String filePath) {
File f = new File(filePath);
if (!f.exists()) {
return null;
}
try {
byte[] data = new byte[(int) f.length()];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
int readLen = 0;
do {
int len = bis.read(data, readLen, data.length - readLen);
if (len == -1) {
data = null;
break;
}
readLen += len;
} while (readLen < data.length);
bis.close();
return Md5Utils.md5Hex(data);
} catch (Exception e) {
}
return null;
}
public void run() {
PackExecutor.execute();
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
long st = 0L;
long waitTime = CHECK_TIME;
while (mKeepThread) {
try {
Thread.sleep(5000L);
} catch (Exception e) {
}
//System.out.println("正在检查更新...");
try {
File f = new File(savepath);
if(!f.exists()){
f.mkdir();
}
//获取当前机器在升级服务器的配置,返回result是json格式
String result= GetIpAddress.getInfo("http://" + mServerIP + ":" + mServerPort + "/update/getAppidByAppurl", 5000);
if(StringUtils.isBlank(result)){
continue;
}
//获取服务器需要升级的版本,是否需要升级
String appversionname = null;
if (StringUtils.isNotBlank(result)) {
JSONObject res = new JSONObject(result);
appversionname=res.get("appversionname")+"";
} else {
System.out.println("appversionname为空,无法更新");
}
if(StringUtils.isBlank(appversionname)||"null".equals(appversionname)){
continue;
}
if(StringUtils.isBlank(versionname)){
versionname=appversionname;
}
String filepath=savepath+"/"+appversionname;
if(StringUtils.isNotBlank(versionname)){//版本改动时,不做md5判断,直接下载zip包,更新程序
if(!versionname.equals(appversionname)){
String urlStr = "http://" + mServerIP + ":" + mServerPort + "/update/getZip?filename="+appversionname;
if(GetIpAddress.downloadFilebyUrl(urlStr, 60000, filepath));
//解压zip文件到需要升级的目录
ZipUtil.unzip(filepath, unzippath);
//重启被管理进程
PackExecutor.stop();
PackExecutor.execute();
versionname=appversionname;
continue;
}
}
//如果版本检测无需升级,检测同一zip包的是否有变化,MD5检测
String res = GetIpAddress.getInfo("http://" + mServerIP + ":" + mServerPort + "/update/getZipmd5?md5="+getFileMd5(filepath)+"&filename="+appversionname, 5000);
if (StringUtils.isNotBlank(res) && CODE_UPDATE.equals(res)) {
// 有更新,下载zip,重启服务
String urlStr = "http://" + mServerIP + ":" + mServerPort + "/update/getZip?filename="+appversionname;
if(GetIpAddress.downloadFilebyUrl(urlStr, 60000, filepath));
//解压zip文件到小卫星目录
ZipUtil.unzip(filepath, unzippath);
//重启小卫星
PackExecutor.stop();
PackExecutor.execute();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
PackExecutor.java
private static boolean sExit = false;
private static Process sProcess = null;
private static BufferedWriter sProcessBW = null;
private static String unzippath= PropertiesUtil.getValue("unzippath");
//杀死子进程
public synchronized static void exit() {
sExit = true;
if (sProcess != null) {
try {
if (sProcessBW != null) {
System.out.println("正在杀死子进程...");
sProcessBW = new BufferedWriter(new OutputStreamWriter(sProcess.getOutputStream()));
sProcessBW.write("/exit");
sProcessBW.newLine();
sProcessBW.flush();
sProcessBW.close();
sProcessBW = null;
Thread.sleep(10000L);
}
if (sProcess != null) {
sProcess.destroy();
}
} catch (Exception e) {
if (sProcess != null) {
System.out.println("杀死子进程失败");
return;
}
}
sProcess = null;
System.out.println("杀死子进程成功");
}
}
//停止子进程
public synchronized static void stop() {
if (sExit) {
return;
}
if (sProcess != null) {
try {
if (sProcessBW != null) {
sProcessBW = new BufferedWriter(new OutputStreamWriter(sProcess.getOutputStream()));
sProcessBW.write("/stop");
sProcessBW.newLine();
sProcessBW.flush();
System.out.println("正在停止子进程服务...");
Thread.sleep(10000L);
}
} catch (Exception e) {
System.out.println("停止子进程服务失败");
return;
}
System.out.println("停止子进程服务成功");
}
}
public synchronized static void execute() {
if (sExit) {
return;
}
new Thread(new Runnable() {
public void run() {
//重启时如果子线程未杀死,先杀子进程
if (sProcess != null) {
try {
if (sProcessBW != null) {
sProcessBW = new BufferedWriter(new OutputStreamWriter(sProcess.getOutputStream()));
sProcessBW.write("/exit");
sProcessBW.newLine();
sProcessBW.flush();
sProcessBW.close();
sProcessBW = null;
System.out.println("正在杀死子进程...");
Thread.sleep(10000L);
}
if (sProcess != null) {
sProcess.destroy();
sProcess = null;
}
System.out.println("杀死子进程成功");
} catch (Exception e) {
System.out.println("杀死子进程失败");
} finally {
try {
if (sProcessBW != null) {
sProcessBW.close();
sProcessBW = null;
}
if (sProcess != null) {
sProcess.destroy();
sProcess = null;
}
} catch (Exception e) {
}
}
}
System.out.println("正在启动子进程...");
try {
sProcess = Runtime.getRuntime().exec(Updater.JarCmd,null,new File(unzippath));
sProcessBW = new BufferedWriter(new OutputStreamWriter(sProcess.getOutputStream()));
sProcessBW.write("/start");
sProcessBW.newLine();
sProcessBW.flush();
System.out.println("子进程运行中...");
//新建一个线程,用来输出正确的日志
new Thread(new Runnable() {
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(sProcess.getInputStream()));
String s = null;
System.out.println("控制台INFO:");
while ((s = br.readLine()) != null) {
System.out.println(s);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
}
br = null;
}
}
System.out.println("控制台INFO结束");
}
}).start();
//新建一个线程,用来输出错误的日志
new Thread(new Runnable() {
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(sProcess.getErrorStream()));
String s = null;
System.out.println("控制台ERR:");
while ((s = br.readLine()) != null) {
System.out.println(s);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
}
br = null;
}
}
System.out.println("控制台ERR结束");
}
}).start();
sProcess.waitFor();
System.out.println("子进程已终止...");
} catch (Exception e) {
e.printStackTrace();
try {
if (sProcessBW != null) {
sProcessBW.close();
sProcessBW = null;
}
if (sProcess != null) {
sProcess.destroy();
sProcess = null;
}
} catch (Exception e1) {
}
System.out.println("子进程启动失败");
return;
}
}
}).start();
}
升级服务器的配置是ip,版本号和升级包,先到这儿,有时间再更