Java多区域部署时能自动升级的一个小演示程序

来源:互联网 发布:淘宝3c证书编号查询 编辑:程序博客网 时间:2024/05/09 16:50

    公司的一个系统,需要在客户的多个不同的区域公司部署,于是每次有点升级的东西时,就要去20几个部署点去替换文件,效率太低,还极易出错。为此,利用JavaAgent功能,实现了一个能自动升级的小模块。    


    本模块具有如下优点:1、不用修改任何现有代码;2、在系统启动的时候,能检测更新并下载;3、不同的区域可以下载不同的文件;4、下载失败的时候能够自动重试,多次失败以后,就用最近一次成功更新的文件运行。    


    本模块同时具有如下优点:仅仅能够在启动的时候才去检查更新。


    JavaAgent的理论就不说了,还是看一下演示代码。演示代码功能不是很强大,也没啥注释,但是,用来演示还是不成问题的。     


    以下为JSP代码,保存为apache-tomcat-8.0.21/webapps/sys/ms/info.jsp,apache-tomcat-8.0.21/webapps/sys/ms/lib下就是所有的jar包了。

<?xml version="1.0" encoding="gbk" ?><jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page"><jsp:directive.page contentType="text/xml; charset=GBK" /><jsp:directive.page import="java.io.*, java.util.*" /><jsp:directive.pageimport="org.w3c.dom.*, javax.xml.parsers.*, javax.xml.transform.*, javax.xml.transform.dom.*, javax.xml.transform.stream.*" /><jsp:scriptlet><![CDATA[String url = request.getRequestURL().toString();long lastModified = -1; try {url = url.substring(0, url.lastIndexOf("/"));Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();Element xml = doc.createElement("xml");doc.appendChild(xml);File file = new File(this.getServletContext().getRealPath("/"),"ms/lib");for (File f : file.listFiles()) {lastModified = lastModified > f.lastModified() ? lastModified : f.lastModified();Element e = doc.createElement("file");// 文件完整的下载地址e.setAttribute("url", url + "/lib/" + f.getName());// 用文件大小来校验,多数情况下能应付,当然,也可考虑用MD5e.setAttribute("checksum", String.valueOf(f.length()));xml.appendChild(e);}// 用最近一个被修改的文件的修改日期当作整个库的版本xml.setAttribute("version", String.valueOf(lastModified));StringWriter writer = new StringWriter();StreamResult result = new StreamResult(writer);TransformerFactory tf = TransformerFactory.newInstance();Transformer transformer = tf.newTransformer();transformer.setOutputProperty(OutputKeys.METHOD, "xml");transformer.setOutputProperty(OutputKeys.ENCODING, "GBK");transformer.setOutputProperty(OutputKeys.INDENT, "yes");transformer.transform(new DOMSource(doc), result);out.println(writer.toString());} catch (Exception e) {out.println("<xml><error><" + "![CDATA[");out.println(url);e.printStackTrace(new PrintWriter(out));out.println("]" + "]></error></xml>");}//test]]></jsp:scriptlet></jsp:root>

    以下为java代码,实现升级功能。

package hbltsl.privacy.javaagent;import java.io.Closeable;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.InputStream;import java.io.RandomAccessFile;import java.lang.instrument.Instrumentation;import java.net.HttpURLConnection;import java.net.URL;import java.util.Properties;import java.util.jar.JarFile;import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;public class WebClassLoader {String config = null;// 基本配置文件,记录的是本地最新的版本,升级服务器的URL地址Properties loadProperties() {Properties p = new Properties();try {String[] files = { "config.properties", "../config.properties","conf/config.properties", "../conf/config.properties" };File file = null;for (String s : files) {file = new File(s);if (file.exists() && file.isFile()) {config = s;p.loadFromXML(new FileInputStream(file));break;}}} catch (Exception e) {e.printStackTrace();}return p;}// 保存配置void saveProperties(Properties p) throws Exception {File file = new File(config == null ? "config.properties" : config);p.storeToXML(new FileOutputStream(file), config);}// 关闭<T extends Closeable> void close(T t) {try {if (t != null)t.close();} catch (Exception e) {e.printStackTrace();}}// 下载最新版到本地目录,支持断点,支持错误重试,支持简单校验void downloadFile(String url, String dest, long checksum) throws Exception {InputStream input = null;RandomAccessFile output = null;String fileName = url.substring(url.lastIndexOf("/") + 1);File file = new File("lib", dest);if (!file.exists()) {file.mkdirs();}file = new File(file, fileName);long pos = file.exists() ? file.length() : -1L;if (pos == checksum) {return;} else if (pos > checksum) {file.delete();pos = -1L;}HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();httpConnection.setRequestProperty("User-Agent", "NetFox");if (pos > 0) {httpConnection.setRequestProperty("RANGE", "bytes=" + file.length());}try {input = httpConnection.getInputStream();output = new RandomAccessFile(file, "rw");if (pos > 0) {output.seek(pos);}byte[] b = new byte[1024];int nRead = 0;while ((nRead = input.read(b, 0, 1024)) > 0) {output.write(b, 0, nRead);}if (file.length() != checksum) {throw new RuntimeException("file size not matchs : expect - "+ checksum + " current - " + file.length());}} finally {close(input);close(output);}}// 获取最新版本String download(String webUrl, String curLibDir) throws Exception {Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(webUrl);Element root = doc.getDocumentElement();String version = root.getAttribute("version");if (version.equals(curLibDir) && new File("lib", version).exists()) {System.out.println(this + " up-to-date");return curLibDir;}NodeList nodeList = root.getChildNodes();for (int i = 0, l = nodeList.getLength(); i < l; i++) {Node node = nodeList.item(i);if (node.getNodeType() == Node.ELEMENT_NODE) {Element element = (Element) node;String url = element.getAttribute("url");long checksum = Long.parseLong(element.getAttribute("checksum"));for (int retry = 15; retry > 0; retry--) {try {downloadFile(url, version, checksum);break;} catch (Exception e) {if (retry == 1) {throw e;}}}}}return version;}void run(Instrumentation inst) throws Exception {Properties prop = loadProperties();String curLibDir = prop.getProperty("cur", null);String webUrl = prop.getProperty("web","http://192.168.1.219:8080/sys/ms/info.jsp");String version = download(webUrl, curLibDir);prop.put("cur", version);saveProperties(prop);File file = new File("lib", version);for (File f : file.listFiles()) {System.out.println("Load jar : " + f);inst.appendToSystemClassLoaderSearch(new JarFile(f));}}public static void premain(String agentOps, Instrumentation inst) {try {new WebClassLoader().run(inst);} catch (Exception e) {e.printStackTrace();}}}

    将java文件单独打包,名为boot.jar,记得在boot.jar中的META-INF\MANIFEST.MF文件中增加一行

Premain-Class: hbltsl.privacy.javaagent.WebClassLoader


     修改META-INF\MANIFEST.MF文件是特别要注意的是,此文件最末尾有一个空行,千万别把原来的空行给删掉了。



0 0