将 jar 文件添加到检测路径
Adding jar file to instrumentation path
我有两个 jar 文件(例如,我们称它们为 Updater.jar 和 Code.jar)。 Updater.jar 使用其 main 方法启动,然后使用 premain 方法再次启动:
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
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 中所有 classes 的列表>,我一直无法完成。
假设 Code.jar 有三个 class 文件:Main.class、writer.class 和 display.class。有没有办法获取他们每个 class 对象的列表,而不是他们的名字?
一个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(…)
. If you intent to instrument JRE classes as well in a way that the instrumented classes need access to the classes of Code.jar
, you’ll have to change the bootstrap path。
请注意,这必须尽早发生:
The Java™ Virtual Machine Specification specifies that a subsequent attempt to resolve a symbolic reference that the Java virtual machine has previously unsuccessfully attempted to resolve always fails with the same error that was thrown as a result of the initial resolution attempt. Consequently, if the JAR file contains an entry that corresponds to a class for which the Java virtual machine has unsuccessfully attempted to resolve a reference, then subsequent attempts to resolve that reference will fail with the same error as the initial attempt.
我有两个 jar 文件(例如,我们称它们为 Updater.jar 和 Code.jar)。 Updater.jar 使用其 main 方法启动,然后使用 premain 方法再次启动:
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
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 中所有 classes 的列表>,我一直无法完成。
假设 Code.jar 有三个 class 文件:Main.class、writer.class 和 display.class。有没有办法获取他们每个 class 对象的列表,而不是他们的名字?
一个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(…)
. If you intent to instrument JRE classes as well in a way that the instrumented classes need access to the classes of Code.jar
, you’ll have to change the bootstrap path。
请注意,这必须尽早发生:
The Java™ Virtual Machine Specification specifies that a subsequent attempt to resolve a symbolic reference that the Java virtual machine has previously unsuccessfully attempted to resolve always fails with the same error that was thrown as a result of the initial resolution attempt. Consequently, if the JAR file contains an entry that corresponds to a class for which the Java virtual machine has unsuccessfully attempted to resolve a reference, then subsequent attempts to resolve that reference will fail with the same error as the initial attempt.