2016-07-05 4 views
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, но для использования этого мне нужно будет получить List> всех классов в банке, которые я не смог выполнить.

Допустим, что Code.jar имеет три файла класса: Main.class, writer.class и display.class. Есть ли способ получить список каждого из объектов класса, а не их имя?

+0

Просто добавьте требуемую банку в путь класса. – ManoDestra

+0

Это только изменение переменной java.library.path? – JD9999

+0

Я имел в виду просто добавив требуемую банку к пути к классам, например. java -cp path/to/your.jar; path/to/other.jar com.example.app.MainApp. Не уверен, что это то, что вы имели в виду? Кроме того, вы можете добавлять JAR динамически во время выполнения через класс URLClassLoader, если вы ищете большую гибкость. – ManoDestra

ответ

2

Агент Java может добавлять файлы jar просто через интерфейс Instrumentation, полученный им в методе запуска, например.

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(…). Если вы намерены использовать JRE-классы так, чтобы инструментальные классы нуждались в доступе к классам Code.jar, вместо этого вам нужно будет change the bootstrap path.

Обратите внимание, что это должно произойти как можно раньше:

The Java ™ Virtual Machine Specification указывает, что последующая попытка разрешить символическую ссылку, что виртуальная машина Java ранее безуспешно пытались решить всегда терпит неудачу с той же ошибкой, которая была вызвана в результате первоначальной попытки разрешения. Следовательно, если в файле JAR содержится запись, соответствующая классу, для которого виртуальная машина Java безуспешно пыталась разрешить ссылку, последующие попытки разрешить эту ссылку не с той же ошибкой, что и при первоначальной попытке.

+0

Итак, если другой класс с тем же именем не загружается, то будет ли класс здесь? – JD9999

+1

Ну, если вы добавите банку в путь к классу, вы предполагаете, что нет другого класса с тем же имя в этой области, так как иначе ваша банка никогда не будет обыскана. Но если попытка загрузить этот класс с этой загрузкой класса er был сделан до того, как вы добавили свою банку, ошибка будет запомнена.Поскольку «premain» работает перед любым кодом приложения, это не должно быть проблемой, если вы добавляете банку, прежде чем предпринимать какие-либо действия, которые могут привести к попытке загрузки. – Holger

+0

Существует основное приложение, которое запускается. Это приложение запускает первую банку (назовем ее start.jar). После запуска start.jar приложение запускает программу обновления. Затем программа обновления запускает метод premain. Таким образом, он запускается до кода приложения, будет ли это иметь значение? – JD9999