java实现的客户端自更新自重启小程序

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,版本号和升级包,先到这儿,有时间再更

  

posted on 2016-02-03 16:51  zbh1990  阅读(643)  评论(0)    收藏  举报