Showing posts with label modify. Show all posts
Showing posts with label modify. Show all posts

Wednesday, 29 April 2020

Javassist: Modify type of instance field

It is a two-step process.
a.   Remove the field
b.   Create a filed with same name with different type

Follow below step-by-step procedure to modify type of a field.

Step 1: Get ClassPool instance.
ClassPool classPool = ClassPool.getDefault();

Step 2: Get CtClass instance.
CtClass pointClass = classPool.get("com.sample.app.model.Point");

Step 3: Remove the field from CtClass instance.
CtField toBeDeleted = pointClass.getField("x");
pointClass.removeField(toBeDeleted);

Step 4: Create field with same with different data type.
CtField xField = new CtField(CtClass.doubleType, "x", pointClass);
xField.setModifiers(Modifier.PUBLIC);

Step 5: Add new field to the class.
pointClass.addField(xField);

Step 6: Publish modified byte code
pointClass.toClass();

Find the below working application.

Point.java
package com.sample.app.model;

public class Point {
    public int x;
    public int y;

}

App.java
package com.sample.app;

import java.lang.reflect.Field;

import com.sample.app.model.Point;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;

public class App {

    public static boolean set(Object object, String fieldName, Object fieldValue) {
        Class<?> clazz = object.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(object, fieldValue);
                return true;
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    public static <V> V get(Object object, String fieldName) {
        Class<?> clazz = object.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return (V) field.get(object);
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
        return null;
    }

    public static void main(String args[]) throws Exception {
        ClassPool classPool = ClassPool.getDefault();

        CtClass pointClass = classPool.get("com.sample.app.model.Point");

        CtField toBeDeleted = pointClass.getField("x");
        pointClass.removeField(toBeDeleted);

        CtField xField = new CtField(CtClass.doubleType, "x", pointClass);
        xField.setModifiers(Modifier.PUBLIC);
        pointClass.addField(xField);

        // Publish modified byte code
        pointClass.toClass();

        pointClass.writeFile("/Users/Shared/javassist");
        Point point = new Point();

        set(point, "x", 1.2345);
        Object value = get(point, "x");

        System.out.println("Value of x is " + value);
    }

}

Run App.java, you will see below message in console.
Value of x is 1.2345

You can observe that Point.class file is created at /Users/Shared/javassist.

$tree /Users/Shared/javassist
/Users/Shared/javassist
└── com
    └── sample
        └── app
            └── model
                └── Point.class

4 directories, 1 file

Open Point.class in any java decompiler to see the source code.



Previous                                                    Next                                                    Home

Tuesday, 21 April 2020

Javassist: Get the modified byte code

Step 1: Get the instance of ClassPool.
ClassPool pool = ClassPool.getDefault();

ClassPool is the root class that controls the byte code modifications. It is a container of CtClass objects (CtClass object represent a class file).

Step 2: Read class file from the source.
CtClass ctClass = pool.get("com.sample.app.model.Employee");

‘pool.get()’ method reads a class file from the source and returns a reference to the CtClass object representing that class file.  If that class file has been already read, this method returns a reference to the CtClass created when that class file was read at the first time.

‘pool.get’ method searches the default system search path to find the class.

Step 3: Set super class to this class.
ctClass.setSuperclass(pool.get("com.sample.app.model.BaseEntity"));

Step 4: Call toBytecode method to get the byte code of class.
byte[] byteCode = ctClass.toBytecode();

Find the below working application.

BaseEntity.java
package com.sample.app.model;

public class BaseEntity {
 private String createdBy;
 private String updatedBy;

 public String getCreatedBy() {
  return createdBy;
 }

 public void setCreatedBy(String createdBy) {
  this.createdBy = createdBy;
 }

 public String getUpdatedBy() {
  return updatedBy;
 }

 public void setUpdatedBy(String updatedBy) {
  this.updatedBy = updatedBy;
 }

}

Employee.java
package com.sample.app.model;

public class Employee {
 private int id;
 private String name;

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

}

App.java

package com.sample.app;

import javassist.ClassPool;
import javassist.CtClass;

public class App {

 public static void main(String args[]) throws Exception {
  ClassPool pool = ClassPool.getDefault();

  CtClass ctClass = pool.get("com.sample.app.model.Employee");
  ctClass.setSuperclass(pool.get("com.sample.app.model.BaseEntity"));

  byte[] byteCode = ctClass.toBytecode();

  String str = new String(byteCode);
  System.out.println(str);

 }
}



Previous                                                    Next                                                    Home