Instrumentation retransformation doesn't appear to be working - java

I'm just experimenting with Java Instrumentation because it's very interesting and I'd like to know more about it. I'm using it in conjunction with the javassist library to make bytecode manipulation much easier, and the "tools" library which is included in JDK install.
Here is my main class:
public class MainClass {
public static boolean first = true;
static{
AgentClass.initialize();
}
public static void loadAgent(){
String path = System.getProperty("user.dir") + "\\AgentJar.jar";
String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
int p = nameOfRunningVM.indexOf('#');
String pid = nameOfRunningVM.substring(0, p);
try {
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(path, "");
vm.detach();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static void main(String[] args){
System.out.println("First run-through, code should be modified once.");
new Hello().hello();
first = false;
try {
AgentClass.getInstrumentation().retransformClasses(Class.forName("test.Hello"));
} catch (Exception e){
e.printStackTrace();
}
System.out.println("Second run-through, code should be modified twice.");
new Hello().hello();
}
}
Here is the "Hello" class:
public class Hello {
public void hello(){
System.out.println("Hello World!");
}
}
Here is the FileTransformer class:
public class FileTransformer implements ClassFileTransformer{
private static boolean first = true;
#Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (!className.contains("Hello"))
return null;
else{
byte[] result;
CtClass cc = null;
try {
cc = ClassPool.getDefault().get("test.Hello");
CtMethod method = cc.getDeclaredMethod("hello");
if (MainClass.first){
System.out.println("In transformer: first");
method.insertAfter("System.out.println(\"Modified First Time!\");");
}else{
System.out.println("In transformer: second");
method.insertAfter("System.out.println(\"I modified it again.!\");");
}
cc.writeFile();
result = cc.toBytecode();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return result;
}
}
}
The agent class is in another jar, it's a basic implementation of it:
public class AgentClass {
protected static Instrumentation inst;
private static boolean added = false;
public static void agentmain(String args, Instrumentation inst){
AgentClass.inst = inst;
if (!added)
inst.addTransformer(new FileTransformer());
}
public static void premain(String args, Instrumentation inst){
AgentClass.inst = inst;
inst.addTransformer(new FileTransformer());
added = true;
}
public static void initialize(){
if (inst == null){
MainClass.loadAgent();
}
}
public static Instrumentation getInstrumentation(){
return inst;
}
}
When I run, I encounter no errors. However, the output is not how I would expect it to be.
Here is the output I get:
First run-through, code should be modified once.
In transformer: first
Hello World!
Modified First Time!
Second run-through, code should be modified twice.
Hello World!
Modified First Time!
You might notice that there is no line that reads "I modified it again!"
Any help is appreciated.

Not sure if there are any other issues here, but if you want to retransform classes, you need to register the ClassFileTransformer using the method that allows you to specify that the transformer can retransform. i.e.
if you call instrumentation.addTransformer(ClassFileTraIsformer), then you are stating that the transformer does not supports retransforms.
You need to call instrumentation.addTransformer(ClassFileTraIsformer, true) and this will make your transformer kick in.

Related

Put java class to nashorn's global scope

I use sandboxed Nashorn like this:
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(
new String[]{"--no-java", "--no-syntax-extensions", "--optimistic-types=true", "--language=es6"},
null);
But I want to use a single particular class in my javascript. How to do that?
For example, I have a class:
class MyClass
{
public void m1()
{
System.out.println("This is m1");
}
public void m2()
{
System.out.println("This is m2");
}
}
And I want to use it in the script like
let a=new MyClass();
a.m1();
a.m2();
How to do that?
Thank you
you can use Bindings to provide java objects to the ScriptEngine:
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class MyClass {
public static void main(String[] args) {
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(
new String[]{"--no-java", "--no-syntax-extensions", "--optimistic-types=true", "--language=es6"},
null);
Bindings bindings = engine.createBindings();
bindings.put("myclass", new MyClass());
engine.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
String code = "myclass.m1();";
try {
engine.eval(code, bindings);
} catch (ScriptException e) {
e.printStackTrace();
}
}
public void m1() {
System.out.println("This is m1");
}
public void m2(String str) {
System.out.println("This is m2 " + str);
}
public void m3(Class claz) {
System.out.println(claz);
}
}
or if MyClass really needs to be instantiated in the JS code you could do (works with Java >=9):
Bindings bindings = engine.createBindings();
bindings.put("myclass", StaticClass.forClass(MyClass.class));
var code = "var a = new myclass(); a.m1();";
try {
engine.eval(code, bindings);
} catch (ScriptException e) {
e.printStackTrace();
}

How to unit test java main method?

I have a class to which I must pass 2 arguments through its main method, if passed less than 2 args, it displays a system error message. I wrote a unit test for the main method here, when I run the test, it stops at "running" (shows neither pass nor fail). Please suggest.
Example.java
public class Example
{
private static String str1 = null;
private static String str2 = null;
public static void main(String[] args)
{
if( args.length != 2 )
{
call();
}
Example ex = new Example(args[0], args[1]);
ex.getData();
}
public Example(String str1, String str2)
{
Example.str1 = str1;
Example.str2 = str2;
}
public void getData(){
System.out.println("Name is: "+str1);
System.out.println("City is: "+str2);
}
private static void call()
{
System.err.println("Usage: String1 String2");
System.err.println("Where: ");
System.err.println(" String1 - Name");
System.err.println(" String1 - City");
System.exit(1);
}
}
ExampleTest.java
public class ExampleTest {
#Test
public void testPassingWrongNumberOfInputs() {
StringBuffer sb = new StringBuffer();
sb.append("Usage: String1 String2")
.append("Where: ")
.append(" String1 - Name")
.append(" String1 - City");
String expectedErrorMessage = sb.toString();
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
System.setErr(new PrintStream(outContent));
String[] args = {"one"};
Example.main(args);
assertEquals(expectedErrorMessage, outContent.toString());
}
}
How about the following:
class TestingSecurityManager extends SecurityManager {
#Override public void checkExit(int status) {
throw new SecurityException();
}
}
Then in your test...
public class ExampleTest {
#Test
public void testPassingWrongNumberOfInputs() {
StringBuffer sb = new StringBuffer();
sb.append("Usage: String1 String2")
.append("Where: ")
.append(" String1 - Name")
.append(" String1 - City");
String expectedErrorMessage = sb.toString();
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
System.setErr(new PrintStream(outContent));
String[] args = {"one"};
TestSecurityManager sm = new TestSecurityManager ();
System.setSecurityManager(sm);
try {
Example.main(args);
//should throw
fail("Should have thrown exception");
} catch (SecurityException se) {
}
assertEquals(expectedErrorMessage, outContent.toString());
}
}
I finally was able to write the unit test as shown in the following. I only tested if the method is hitting System.exit(1) code or not.
public class ExampleTest {
private SecurityManager m;
private TestSecurityManager sm;
#Before
public void setUp()
{
m = System.getSecurityManager();
sm = new TestSecurityManager ();
System.setSecurityManager(sm);
}
#After
public void tearDown()
{
System.setSecurityManager(m);
}
#Test
public void testPassingWrongNumberOfInputs() {
try {
Example.main(new String[] {"one"});
} catch (SecurityException se) {
assertEquals("1", se.getMessage());
}
}
}
class TestSecurityManager extends SecurityManager {
#Override
public void checkPermission(Permission permission) {
if ("exitVM".equals(permission.getName()))
{
throw new SecurityException("System.exit attempted and blocked.");
}
}
#Override
public void checkExit(int status) {
throw new SecurityException(Integer.toString(status));
}
}
Remove the System.exit(1) call, you don't need it. Your app will exit after main() completes anyway without an unneeded call to explicitly terminate the VM. This call is most likely causing your JUnit to stop executing before you get to your assertEquals statement, because you just told the VM to quit.
Rename the main method, and add a return value, so you can test it. Call this new method from main.

Java - Existence of method causing program to not work correctly

In my Java program, I have a class called Car, which is Serializable. I have another class called StaffCar which is a subclass of Car.
Then there a class called Fleet which essentially stores StaffCar objects in ArrayList<StaffCar> fleet.
I then have a class Main which consists of the main method which consists of a menu and a switch to handle menu options.
The problem I'm having is when I add a method in StaffCar, even if the method has nothing inside and the method isn't even called, one of the menu options which is 'Display all car information', stops working.
If I comment out this method, it starts working again.
The 'Display...' option calls printCars()from Fleet which has fleet loaded with StaffCar objects from the serialized file, it's like the existence of this method stops the file from even being read.
Snippet of Car
import java.util.ArrayList;
import java.io.*;
#SuppressWarnings("serial")
public class Car implements Serializable
{
//attributes for Car
String regNo;
String model;
int mileage;
//default constructor
public Car() throws CarException
{
try
{
setRegNo("??????");
setModel("Unknown");
setMileage(0);
}
catch (CarException c)
{
System.out.println(c.getMessage());
}
}
//setters
public void setRegNo(String regNo) throws CarException
{
if (regNo.isEmpty())
{
throw new CarException("\nInvalid registration number!\n");
}
this.regNo = regNo;
}
public void setModel(String model) throws CarException
{
if (model.isEmpty())
{
throw new CarException("\nModel can't be empty!\n");
}
this.model = model;
}
public void setMileage(int mileage) throws CarException
{
if (mileage < 0)
{
throw new CarException("\nInvalid mileage!");
}
this.mileage = mileage;
}
}
Snippet of StaffCar, where the problem is being caused
import java.util.ArrayList;
#SuppressWarnings("serial")
public class StaffCar extends Car
{
String staffName;
String availability;
public StaffCar() throws CarException
{
super();
try
{
setAvailability("Available");
setStaffName("");
}
catch (CarException c)
{
System.out.println(c.getMessage());
}
}
public void setStaffName(String staffName)
{
this.staffName = staffName;
}
public void setAvailability(String availability) throws CarException
{
if (availability != "Available" && availability != "Borrowed")
{
throw new CarException("\nInvalid borrow status!\n");
}
this.availability = availability;
}
//this method causing issues, even if empty
/*public void returnCar()
{
}*/
}
Snippet of Fleet class
import java.util.ArrayList;
import java.io.*;
public class Fleet
{
//declare container
ArrayList<StaffCar> fleet;
//container to hold regNos
ArrayList<String> regNumbers;
//create constructor
public Fleet()
{
fleet = new ArrayList<StaffCar>();
regNumbers = new ArrayList<String>();
}
//add method
public void addCar(StaffCar car)
{
fleet.add(car);
regNumbers.add(car.regNo);
}
//print all cars' details
public void printCars()
{
for (StaffCar car:fleet)
{
System.out.println(car);
}
}
public void saveAs(String fileName) throws CarException
{
FileOutputStream outputFile;
try
{
outputFile = new FileOutputStream(fileName);
}
catch (IOException io)
{
throw new CarException("\nCannot create " + fileName + "\n");
}
ObjectOutputStream fleetFile;
try
{
fleetFile = new ObjectOutputStream(outputFile);
fleetFile.writeObject(regNumbers);
fleetFile.writeObject(fleet);
fleetFile.close();
}
catch (FileNotFoundException e)
{
throw new CarException("\nCannot create " + fileName + "\n");
}
catch (IOException io)
{
throw new CarException("\nCannot write " + fileName + "\n");
}
}
#SuppressWarnings({ "unchecked", "resource" })
public void open(String fileName) throws CarException
{
FileInputStream inputFile;
try
{
inputFile = new FileInputStream(fileName);
}
catch (FileNotFoundException e)
{
throw new CarException("\nCannot open " + fileName + "\n");
}
ObjectInputStream fleetFile;
try
{
fleetFile = new ObjectInputStream(inputFile);
regNumbers = (ArrayList<String>)fleetFile.readObject();
fleet = (ArrayList<StaffCar>)fleetFile.readObject();
}
catch (IOException io)
{
throw new CarException("\nError reading from " + fileName + "\n");
}
catch (ClassNotFoundException e)
{
throw new CarException("\nError reading from " + fileName + "\n");
}
try
{
fleetFile.close();
}
catch (IOException io)
{
throw new CarException("\nCannot close " + fileName + "\n");
}
}
}
I apologise for what seems like me dumping a bunch of code to you, I know this is bad practice and I have tried to condense the code as much as I can, but I feel like this is all the relevant code to my problem.
Like I said, I don't understand why the simple addition of an empty method is causing this issue.
EDIT
Main class
public class Main
{
// new container
static Fleet fleet = new Fleet();
// initialise car object
static StaffCar car;
// programme loop variable
static boolean state = false;
String fileName;
public static void main(String[] args) throws CarException
{
start();
// programme loop
while (!state)
{
try
{
// menu option variable
String option;
//displays menu to user and takes in input
option = Console.askString("Menu:\n1 Add a car\n2 Display all car information\n3 Find a car\n4 Borrow a car\n5 Return a car\n6 Exit\n\n");
//removes white spaces
option = option.trim();
//switch to handle user request
switch (option)
{
//if option 1
case "1":
//call static add car method
addMethod();
break;
//if option 2
case "2":
//call static print car method
displayMethod();
break;
//..option 3
case "3":
//call static find car method
findMethod();
break;
//..option 4
case "4":
borrowMethod();
break;
case "5":
//returnMethod();
break;
case "6":
//call static quit method
quitMethod();
break;
default:
System.out.println();
System.out.println("Invalid option.");
System.out.println();
break;
}
}
catch (CarException c)
{
System.out.println(c.getMessage());
}
}
}
public static void start()
{
try
{
fleet.open("fleet.uwl");
}
catch (CarException e)
{
//System.out.println("\nFile not created yet!\n");
}
}
//static menu method to print cars
public static void displayMethod() throws CarException
{
System.out.println();
//call printCars method
fleet.printCars();
System.out.println();
}
}
You saved instances of StaffCar using serialization, then changed the StaffCar class, and are unable to read the saved StaffCar again.
That's because, if you don't specify a serialVersionUID in your class, the JVM computes one for you, based on the layout of the class (fields, methods, etc.). So, to temporarily fix your problem, examine the IOException thrown when reading the file, which should tell you what the serialVersionUID of the saved classes are, and add the following to your class:
private static final long serialVersionUID = XXXL;
where XXX is the serial version UID in the saved objects, which should be mentioned in the exception stack trace.
But really, you have these problems because you chose to use serialization for long term storage, which makes your code very hard to evolve. I wouldn't do that. Instead, I would choose a less fragile and easier to evolve format such as JSON or XML. Define what the file should contain, and generate a JSON/XML document containing this data. Then, whatever your future casses look like, as long as you still can parse JSON/XML, you'll be able to read the files and get the saved data.

Loading a class twice in JVM using different loaders

I have one question regarding concepts of class loading. How to load a .class file twice in JVM. I am also writing an excerpt of code that I have written to accomplish this..
1) Loader 1 code
public class MyClassLoader extends ClassLoader {
public MyClassLoader(){
super(MyClassLoader.class.getClassLoader());
}
public Class loadClass(String classname){
try {
return super.loadClass(classname);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
2) Loader 2 code
public class AnotherClassLoader extends ClassLoader {
public AnotherClassLoader(){
super(AnotherClassLoader.class.getClassLoader());
}
public Class loadClass(String classname){
try {
return super.loadClass(classname);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
3) Now i am loading a class named A using this two different class loaders. I suppose the operation classA1==newClassA should return false. Here is the code:
public static void main(String[] args) {
MyClassLoader loader1 = new MyClassLoader();
AnotherClassLoader newLoader = new AnotherClassLoader();
System.out.println("Load with Custom Class Loader instance");
Class classA1 = loader1.loadClass("com.hitesh.coreJava.A");
System.out.println("Class Loader:::"+classA1.getClassLoader());
Class newClassA = newLoader.loadClass("com.hitesh.coreJava.A");
System.out.println("Class Loader:::"+newClassA.getClassLoader());
System.out.println(classA1==newClassA);
System.out.println(classA1.hashCode() + " , " + newClassA.hashCode());
}
4) Result of executing above code:
Load with Custom Class Loader instance
Class Loader:::sun.misc.Launcher$AppClassLoader#11b86e7
Class Loader:::sun.misc.Launcher$AppClassLoader#11b86e7
true
1641745 , 1641745
Could you please explain this
Try this
public class Test1 {
static class TestClassLoader1 extends ClassLoader {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (!name.equals("Test1")) {
return super.loadClass(name);
}
try {
InputStream in = ClassLoader.getSystemResourceAsStream("Test1.class");
byte[] a = new byte[10000];
int len = in.read(a);
in.close();
return defineClass(name, a, 0, len);
} catch (IOException e) {
throw new ClassNotFoundException();
}
}
}
public static void main(String[] args) throws Exception {
Class<?> c1 = new TestClassLoader1().loadClass("Test1");
Class<?> c2 = new TestClassLoader1().loadClass("Test1");
System.out.println(c1);
System.out.println(c2);
System.out.println(c1 == c2);
}
}
output
class Test1
class Test1
false
Both classloaders start the lookup in their parent classloader (that's what the super() call is about). So actually the super classloader loads it in both cases.
You can try this:
String pathToJar = "C:\\path\\to\\my.jar";
String className = "com.mypackage.ClassA";
URLClassLoader cl1 = new URLClassLoader(new URL[] { new URL(pathToJar) });
URLClassLoader cl2 = new URLClassLoader(new URL[] { new URL(pathToJar) });
Class<?> c1 = cl1.loadClass(className);
Class<?> c2 = cl2.loadClass(className);
System.out.println(c1);
System.out.println(c2);
System.out.println(c1==c2 ? "Parent classloader loads" : "Parent classloader does not load");
cl1.close();
cl2.close();
Make sure that my.jar is NOT on your classpath.
In both cases you are using the same ClassLoader to perform the loading. You have two ClassLoaders but each just call super.loadClass() which delegates to the same parent ClassLoader which is AppClassLoader.

Problem when using code from a jar file

I run the following code:
public class Sign {
private static final PrivateKey priv = Util.loadPrivate();
private static final PublicKey pub = Util.loadPublic();
private static final HexBinaryAdapter adp = new HexBinaryAdapter();
public static String sign(String in) {
try {
Signature sign = Signature.getInstance(Util.ALG);
sign.initSign(priv);
sign.update(in.getBytes());
return adp.marshal(sign.sign());
} catch (Exception e) {e.printStackTrace();}
return null;
}
public static boolean verify(String data, String sign) {
try {
Signature verify = Signature.getInstance(Util.ALG);
verify.initVerify(pub);
verify.update(data.getBytes());
return verify.verify(adp.unmarshal(sign));
} catch (Exception e) {e.printStackTrace();}
return false;
}
}
and the main function looks like this:
public static void main(String[] args) {
String in = "lala";
String sign = Sign.sign(in);
System.out.println(sign);
System.out.println(Sign.verify(in, sign));
}
Everything goes well when I run it from within Eclipse (the output is "true"), but when I pack it into a jar (without the main function) and run it then the output is false.
This is how I load the keys:
public static PrivateKey loadPrivate() {
try {
URLConnection con = Util.class.getResource("private.key").openConnection();
byte[] bs = new byte[con.getContentLength()];
con.getInputStream().read(bs);
return KeyFactory.getInstance(ALG).generatePrivate(new PKCS8EncodedKeySpec(bs));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static PublicKey loadPublic() {
try {
URLConnection con = Util.class.getResource("public.key").openConnection();
byte[] bs = new byte[con.getContentLength()];
con.getInputStream().read(bs);
return KeyFactory.getInstance(ALG).generatePublic(new X509EncodedKeySpec(bs));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I checked and loading the keys works fine.
Any idea ?
Just run like this:
java Main -classpath=/path/to/libraryk.jar

Categories

Resources