2016-07-05 15 views
2

2つのjarファイルがあります(例:Updater.jarとCode.jar)。 Updater.jarは、その主な方法で起動し、それはpremainメソッドで再び自分自身を起動しますインストルメンテーションパスにjarファイルを追加する

package Update; 

import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.util.ArrayList; 
import java.util.List; 

public class InstructionLauncher { 

    private List<UpdateInstruction> instructions = new ArrayList<UpdateInstruction>(); 
    private static InstructionLauncher instance; 
    private Process process; 

    public static InstructionLauncher initialise(){ 
     if(instance !=null) return instance; 
     else return new InstructionLauncher(); 
    } 

    public void registerPremain(UpdateInstruction inst){ 
     instructions.add(inst); 
    } 

    public void launchNext(){ 
     UpdateInstruction inst = instructions.get(0); 
     String cls = inst.getClassName() + "." + inst.getMethodName(); 
     String[] args = new String[]{"java", "-javaagent", "JOSUpdater.jar", "-jar", inst.getClassName() + "." + inst.getMethodName()}; 
     ProcessBuilder builder = new ProcessBuilder(args); 
     try { 
      exportResource(cls, cls); 
     } catch (Exception e) { 
      UpdateManager.revert(); 
     } 
     try { 
      Process p = builder.start(); 
      process = p; 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     while(!process.isAlive())launchNext(); 
    } 

    private InstructionLauncher(){ 
     instance = this; 
    } 

    //From http://stackoverflow.com/questions/10308221/how-to-copy-file-inside-jar-to-outside-the-jar 
    private String exportResource(String resourceName, String clazz) throws Exception { 
     InputStream stream = null; 
     OutputStream resStreamOut = null; 
     String jarFolder; 
     try { 
      stream = Class.forName(clazz).getResourceAsStream(resourceName);//note that each/is a directory down in the "jar tree" been the jar the root of the tree 
      if(stream == null) { 
       throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file."); 
      } 

      int readBytes; 
      byte[] buffer = new byte[4096]; 
      jarFolder = new File(Class.forName(clazz).getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getPath().replace('\\', '/'); 
      resStreamOut = new FileOutputStream(jarFolder + resourceName); 
      while ((readBytes = stream.read(buffer)) > 0) { 
       resStreamOut.write(buffer, 0, readBytes); 
      } 
     } catch (Exception ex) { 
      throw ex; 
     } finally { 
      stream.close(); 
      resStreamOut.close(); 
     } 

     return jarFolder + resourceName; 
    } 

} 

premainメソッドは、現時点では次のようになります。

package Update; 

import java.lang.instrument.Instrumentation; 

public class PremainLauncher { 

    public static void premain(String args, Instrumentation inst){ 
     inst.addTransformer(new Transformer(), true); 
     System.out.println("Registered instruction for package: " + args); 
    } 

} 

私は疑問に思って何、外部JAR全体(この例ではCode.jar)を計測器のパスに追加するにはどうすればよいですか?

私はInstrumentation.retransformClassesメソッドについて知っていますが、私が完了できなかったjarファイル内のすべてのクラスのList>を取得する必要があります。

Code.jarには、Main.class、writer.class、display.classという3つのクラスファイルがあります。彼らの名前ではなく、それぞれのクラスオブジェクトのリストを取得する方法はありますか?

+0

クラスパスに必要なjarファイルを追加するだけです。 – ManoDestra

+0

これはjava.library.path変数を変更するだけですか? – JD9999

+0

私はあなたの必要なjarをクラスパスに単に追加することを意味しました。 java -cp path/to/your.jar; path/to/other.jar com.example.app.MainApp。これがあなたが意味するものなのかどうか分からないのですか?それ以上の柔軟性を求めるなら、実行時にURLClassLoaderクラスを介して動的にJARを追加することができます。 – ManoDestra

答えて

2

Javaエージェントは、スタートアップメソッドで受信したInstrumentationインターフェイス経由でjarファイルを追加することができます。

import java.io.IOException; 
import java.lang.instrument.Instrumentation; 
import java.util.jar.JarFile; 

public class PremainLauncher { 
    public static void premain(String args, Instrumentation inst) throws IOException{ 
     inst.appendToSystemClassLoaderSearch(new JarFile("Code.jar")); 
     inst.addTransformer(new Transformer(), true); 
     System.out.println("Registered instruction for package: " + args); 
    } 
} 

Instrumentation.appendToSystemClassLoaderSearch(…)を参照してください。計装クラスがCode.jarのクラスにアクセスする必要があるように、JREクラスを計測する場合は、代わりにchange the bootstrap pathにする必要があります。

のJava™仮想マシン仕様が、その後の試みは、Java仮想マシンが以前に失敗した解決しようとしたことを象徴参照を解決することを指定します。これは、可能な限り早期に起こることがあること

注意最初の解決試行の結果としてスローされたのと同じエラーで常に失敗します。そのため、JARファイルに、Java仮想マシンが参照の解決に失敗したクラスに対応するエントリが含まれている場合、その参照を解決しようとする後続の試行は、最初の試行と同じエラーで失敗します。

+0

同じ名前の別のクラスが読み込まれない場合は、ここにクラスがありますか? – JD9999

+1

クラスパスにjarを追加すると、同じクラスの他のクラスがないとみなしますそのスコープの名前、そうでなければあなたのjarは決して検索されませんが、このクラスでそのクラスをロードしようとするとあなたがあなたの瓶を付け加える前に作られた、失敗は記憶されるでしょう。'premain'はアプリケーションコードの前に実行されるので、読み込みにつながる可能性のあるアクションを実行する前にjarを追加する限り、これは問題ではありません。 – Holger

+0

起動する主なアプリケーションがあります。このアプリケーションは、最初のjarを起動します(start.jarとしましょう)。 start.jarが完了すると、アプリケーションはアップデータを起動します。その後、アップデータはそれ自体でpremainメソッドを起動します。したがって、アプリケーションコードの前に実行されますが、これは違いがありますか? – JD9999

関連する問題