I have asked this question on StackOverflow but this group might be a better forum:
From my understanding, you cannot add methods to a class once it has been loaded by a classloader (in other words redefine a class). I have tried different approaches including using an agent but to no avail.
I have a base class which defines a couple of static methods in order to implement the active record pattern:
public class ActiveRecord {
private Long id;
// getters and setters
private static IllegalStateException implementationMissing() {
return new IllegalStateException(
"This method must be overridden in subclasses");
}
public static Long count(){
throw implementationMissing();
}
public static <T extends ActiveRecord> List<T> findAll(){
throw implementationMissing();
}
public static <T extends ActiveRecord> T findById(Object id){
throw implementationMissing();
}
public static void deleteAll(){
throw implementationMissing();
}
public void save(){
throw implementationMissing();
}
public void delete(){
throw implementationMissing();
}
}
For any given class extending active ActiveRecord, I am trying to implement the methods defined by ActiveRecord using ByteBuddy. For example:
class MapText extends ActiveRecord {}
@Test
void testRedefine(){
ByteBuddyAgent.install();
new ByteBuddy().redefine(MapText.class)
.defineMethod("save", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.defineMethod("count", Long.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.make()
.load(MapText.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
MapText mapText = new MapText();
mapText.save();
MapText.count();
}
However the code displayed above generates the following exception:
java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
I also tried another approach, which consists of using a type pool:
// Spring boot application
public static void main(String[] args) {
TypePool typePool = TypePool.Default.ofSystemLoader();
new ByteBuddy()
.redefine(typePool.describe("pkg.MapText").resolve(),
ClassFileLocator.ForClassLoader.ofSystemLoader())
.defineMethod("save", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.defineMethod("count", Long.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
.make()
.load(ClassLoader.getSystemClassLoader());
SpringApplication.run(AdmService.class, args);
}
However this time I get the following exception:
java.lang.IllegalStateException: Class already loaded: pkg.MapText
In both cases it seems that the class is already loaded by the class loader and I cannot redefine it.
My understanding is that my only option would be to redefine the classes at compile time using the maven plugin
However I can't find any sample code showing how to proceed (I am unfamiliar with maven plugins).
Any help explaining how to proceed in order to make this work would be greatly appreciated (with a last resort using a plugin)!
Thanks,
Anwar