2012-11-15 65 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。

通過將啓動類加載器的程序的一部分放入單獨的方法中,我可以讓所有系統確定沒有更多的引用存在。

重新編寫代碼後,它現在可以工作:)