2012-11-15 29 views
1

目标是让一个简单的Java类启动一个jar main-class。当主要类完成时,可以询问是否要重新加载。通过这种方法,它可以自行更新并重新运行。小班允许一个java程序自动更新自己

启动器是通过URLClassloader加载jar,然后卸载/重新加载更改后的jar。该jar可以通过对Launcher的修改或通过调用提供的dos/unix脚本来更改,该脚本将新jar替换为旧jar的位置。

整个程序如下。经过测试,它似乎工作顺利。

java Launcher -jar [path_to_jar] -run [yourbatchfile] [-runonce]?可选]

1)启动程序在你的jar文件中查找“Main-Class”属性,这样它就不需要给实际的类运行。

2)将调用公共静态无效的主要(字串[] args)“,并通过它,你提供的命令行

3)当你的程序完成后,启动程序将调用在剩余的参数你程序方法'公共静态布尔reload()',如果结果是'真',这将触发重新加载。

4)如果您指定了-runonce,那么程序将不会重新加载。 5)如果指定了-run [batchfile],那么批处理文件将在重新加载之前运行。

我希望这对一些人有帮助。

快乐编码!

import java.io.File; 
import java.io.IOException; 
import java.lang.ProcessBuilder.Redirect; 
import java.lang.reflect.Method; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 
import java.util.concurrent.atomic.AtomicBoolean; 
import java.util.jar.Attributes; 
import java.util.jar.Manifest; 


public class Launcher { 

    public static void main(String[] args) { 
     new Launcher().run(new ArrayList<>(Arrays.asList(args))); 
    } 

    private void run(List<String> list) { 
     final String jar = removeArgPairOrNull("-jar", list); 
     final boolean runonce = removeArgSingle("-runonce", list); 
     final String batchfile = removeArgPairOrNull("-run", list); 

     if (jar == null) { 
      System.out.println("Please add -jar [jarfile]"); 
      System.out.println("All other arguments will be passed to the jar main class."); 
      System.out.println("To prevent reloading, add the argument to -runonce"); 
      System.out.println("To provide another program that runs before a reload, add -run [file]"); 
     } 

     boolean reload; 

     do { 
      reload = launch(list.toArray(new String[0]), new String(jar), new String(batchfile), new Boolean(runonce)); 
      System.out.println("Launcher: reload is: " + reload); 

      gc(); 

      if (reload && batchfile != null) { 
       try { 
        System.err.println("Launcher: will attempt to reload jar: " + jar); 
        runBatchFile(batchfile); 
       } catch (IOException | InterruptedException ex) { 
        ex.printStackTrace(System.err); 
        System.err.println("Launcher: reload batchfile had exception:" + ex); 
        reload = false; 
       } 
      } 

     } while (reload); 

    } 

    private boolean launch(String[] args, String jar, String batchfile, boolean runonce) { 

     Class<?> clazz = null; 
     URLClassLoader urlClassLoader = null; 
     boolean reload = false; 

     try { 
      urlClassLoader = new URLClassLoader(new URL[]{new File(jar).toURI().toURL()}); 

      String mainClass = findMainClass(urlClassLoader); 
      clazz = Class.forName(mainClass, true, urlClassLoader); 

      Method main = clazz.getMethod("main", String[].class); 
      System.err.println("Launcher: have method: " + main); 

      Method reloadMethod; 

      if (runonce) { 
       // invoke main method using reflection. 
       main.invoke(null, (Object) args); 
      } else { 
       // find main and reload methods and invoke using reflection. 
       reloadMethod = clazz.getMethod("reload"); 

       main.invoke(null, (Object) args); 
       System.err.println("Launcher: invoked: " + main); 

       reload = (Boolean) reloadMethod.invoke(null, new Object[0]); 
      } 
     } catch (final Exception ex) { 
      ex.printStackTrace(System.err); 
      System.err.println("Launcher: can not launch and reload this class:" + ex); 
      System.err.println("> " + clazz); 
      reload = false; 
     } finally { 
      if (urlClassLoader != null) { 
       try { 
        urlClassLoader.close(); 
       } catch (IOException ex) { 
        ex.printStackTrace(System.err); 
        System.err.println("Launcher: error closing classloader: " + ex); 
       } 
      } 
     } 

     return reload ? true : false; 
    } 

    private static String findMainClass(URLClassLoader urlClassLoader) throws IOException { 
     URL url = urlClassLoader.findResource("META-INF/MANIFEST.MF"); 
     Manifest manifest = new Manifest(url.openStream()); 
     Attributes attr = manifest.getMainAttributes(); 
     return attr.getValue("Main-Class"); 
    } 

    private static void runBatchFile(String batchfile) throws IOException, InterruptedException { 
     System.out.println("Launcher: executng batchfile: " + batchfile); 
     ProcessBuilder pb = new ProcessBuilder("cmd", "/C", batchfile); 
     pb.redirectErrorStream(true); 
     pb.redirectInput(Redirect.INHERIT); 
     pb.redirectOutput(Redirect.INHERIT); 
     Process p = pb.start(); 
     p.waitFor(); 
    } 

    private static String removeArgPairOrNull(String arg, List<String> list) { 
     if (list.contains(arg)) { 
      int index = list.indexOf(arg); 
      list.remove(index); 
      return list.remove(index); 
     } 
     return null; 
    } 

    private static boolean removeArgSingle(String arg, List<String> list) { 
     if (list.contains(arg)) { 
      list.remove(list.indexOf(arg)); 
      return true; 
     } 
     return false; 
    } 

    private void gc() { 
     for (int i = 0; i < 10; i++) { 
      byte[] bytes = new byte[1024]; 
      Arrays.fill(bytes, (byte) 1); 
      bytes = null; 
      System.gc(); 
      System.runFinalization(); 
     } 
    } 

} 
+0

哪些代码抱怨清单?确切的错误信息是什么样的? findMainClass是做什么的? – MvG

+0

findMainClass只是查看jar清单来查找主类。 我会重新发布整个程序。 –

+0

在urlclassloader重新加载消息,它找不到方法(No SuchMethod)之后失败:clazz.getMethod(“reload”) 也许你可以尝试它并查看错误的位置,或者这可能是一些限制处理和重新加载urlclassloader?/ –

回答

0

调试完成后,我确定原来的困难在于启动器没有隔离UrlClassloader。

通过将启动类加载器的程序的一部分放入单独的方法中,我可以让所有系统确定没有更多的引用存在。

重新编写代码后,它现在可以工作:)