I would like to use Byte Buddy together with OSGi weaving hook.
For instance, it is possible to use Javassist together with OSGi weaving hook like this:
//... other imports
import org.osgi.framework.hooks.weaving.WeavingHook;
import org.osgi.framework.hooks.weaving.WovenClass;
#Component (immediate = true)
public class MyWeavingHook implements WeavingHook {
#Activate
public void activate(ComponentContext ctx) {
System.out.print("Activating demo weaving hook...");
}
#Override
public void weave(WovenClass wovenClass) {
System.out.println("Weaving hook called on " + wovenClass.getClassName());
if (wovenClass.getClassName().equals("DecoratedTestServiceImpl")) {
try (InputStream is = new ByteArrayInputStream(wovenClass.getBytes())) {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass(is);
ctClass.getDeclaredMethod("ping").setBody("return \"WAIVED\";");
wovenClass.setBytes(ctClass.toBytecode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
How to process the wovenClass with Byte Buddy? I see that I can get the bytecode out like this:
byte[] classBytes = new ByteBuddy()
.subclass(AClass.class)
.name("MyClass")
.method(named("theMethod"))
.intercept(FixedValue.value("Hello World!"))
.make()
.getBytes();
wovenClass.setBytes(classBytes);
But I cannot see how to provide the wovenClass bytecode as an input to Byte Buddy. I would need something like:
new ByteBuddy().rebase(wovenClass.getBytes())...
The rebase method is overloaded and accepts a ClassFileLocator as a second argument. You can provide the class bytes directly by providing an explicit mapping:
ClassFileLocator.Simple.of(wovenClass.getTypeDescription().getName(), wovenClass.getBytes())
Related
I am writing a java agent to instrument user annotated methods. Currently, with javassist, I can identify the annotated methods and insert logging information. However, I am wondering how I can instrument the methods to call java agent callbacks. Following is the transform method. I want to call a custom agent method by insertBefore and insertAfter.
#Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
try {
ClassPool classPool = scopedClassPoolFactory.create(loader, rootPool,
ScopedClassPoolRepositoryImpl.getInstance());
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
CtMethod[] methods = ctClass.getDeclaredMethods();
for (CtMethod method : methods) {
Annotation annotation = getAnnotation(method);
if (annotation != null) {
log.info("Going to Transform the class " + className);
method.insertAfter("System.out.println(\"Logging using Agent\");");
}
}
byteCode = ctClass.toBytecode();
ctClass.detach();
} catch (Throwable ex) {
log.log(Level.SEVERE, "Error in transforming the class: " + className, ex);
}
return byteCode;
}
I wanted to post the answer to my question in case it helps others. I created a singleton TraceTool class with TraceStart and TraceEnd methods.
package com.example.monitoring;
public class TraceTool {
private static TraceTool ins = new TraceTool();
public static TraceTool getInstance() {
return ins;
}
public void traceStart(){
System.out.println("Hello!!!! This is Trace Tool: We intercepted function start!!!!!!");
}
public void traceEnd(){
System.out.println("This is Trace Tool: We intercepted function End! Bye!!!!!");
}
}
Then I inject these methods using javassist.
method.insertAfter("com.example.monitoring.TraceTool.getInstance().traceStart();");
method.insertAfter("com.example.monitoring.TraceTool.getInstance().traceEnd();");
Actually i am having a spring main class as follows.
ClassLoader loader = null;
try {
loader = URLClassLoader.newInstance(new URL[]{new
File(plugins + "/" + pluginName + "/" + pluginName +
".jar").toURI().toURL()}, getClass().getClassLoader());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Class<?> clazz = null;
try {
clazz = Class.forName("com.sample.Specific", true, loader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method method = null;
try {
method = clazz.getMethod("run",new Class[]{});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
method.invoke(clazz.newinstance,new Object[]{});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Specific Class is follow :
package com.sample
#Service
public class Specific {
#Autowired
private FD fd;
public void run(){
fd.init();
}
}
#Autowired FD comes to be null. Can anyone give me some solution as i also know new operator will not work for #autowired. As i am loading class with new instance then only it becomes null. Can anyone guide me in this thing
Spring has its own way to provide you new objects. As long as you're consistent using #Autowired and #Component/#Service/#Repository/#Controller there should be no problem
And since all "business" object instantiation is handled by Spring you should never use new. If you have no other way of getting an instance (something I realy doubt about it) you can use ApplicationContext.getBean() but as I said, in most cases this is not required (and this is also a bad practice)
If you need several instances of a class instead of injecting them (by using #Autowired) you can inject a Provider<T>
UPDATE
Since the class is known at runtime you need to inject an ApplicationContext and use it to get the bean:
public class TheClassWhereYouAreCreatingTheObject {
#Autowired
private ApplicationContext context; // You definitely need this
public void theMethodWhereYouAreCreatingTheObject() {
Class<?> clazz = ... // getting the object class
Object instance = context.getBean(clazz); // getting and instance trough Spring
// If you know that kind of object you will get cast it at call its methods
((Specific) instance).run();
// If you know anything about the class you will have to use reflection
Method method = clazz.getMethod("run", new Class[]{});
method.invoke(instance, new Object[]{});
}
}
Add Specific Service bean inside your main class. As long as the service is inside one your component scan packages then you shall be fine. Do not use new operator.
#Autowired
private Specific specific;
If you want to take advantage of autowiring then I think we have to think from spring terms.
you can use Beanutils to create a new instance and play with reflections supporting spring features.
Please go through below methods:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html
Its is my first time implementing a java agent and im trying to learn something about bytecode instrumentation. After reading several introductions and tutorials i coded a small Application with two classes (Summer and Application). Now i want to run a java agent via premain method to show the execution path using the following code:
public class TestJavaAgent {
public static void premain(String agentArgument,
Instrumentation instrumentation){
instrumentation.addTransformer(new ClassFileTransformer() {
#Override
public byte[] transform(ClassLoader classLoader, String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
ClassPool cp = ClassPool.getDefault();
try {
CtClass cc = cp.get("Summer");
CtMethod methods [] = cc.getMethods();
for( CtMethod method : methods){
System.out.println("Entering "+method.getName());
method.addLocalVariable("elapsedTime", CtClass.longType);
method.insertBefore("elapsedTime = System.currentTimeMillis();");
method.insertAfter("{elapsedTime = System.currentTimeMillis() - elapsedTime;"
+ "System.out.println(\"Method Executed in ms: \" + elapsedTime);}");
}
return cc.toBytecode();
} catch (Exception ex) {
return bytes;
}
}
});
}
}
I started the Agent via java -javaagent{Agent JAR} -jar {Application Jar} but it did not print anything of the inserted messages. After debugging the code i realized everything after "ClassPool.getDefault()" will not be reached but i dont know why. Can someone help me?
The transformer is supposed to transform the class being passed as parameter, not some arbitrary class you like. And after registration, it will be called for all classes that are loaded, including the classes you are using yourself (ClassPool at first). So you are creating a circular dependency.
You have to check the class name argument and wait until your method has been called for the class you want to transform. For all other classes, just return null.
public class TestJavaAgent {
public static void premain(String agentArgument, Instrumentation instrumentation) {
instrumentation.addTransformer(new ClassFileTransformer() {
#Override
public byte[] transform(ClassLoader classLoader,
String className, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
if(!className.equals("Summer")) return null;
ClassPool cp = ClassPool.getDefault();
try {
// use the class bytes your received as parameter
cp.insertClassPath(new ByteArrayClassPath(className, bytes));
CtClass cc = cp.get("Summer");
CtMethod[] methods = cc.getMethods();
for( CtMethod method : methods){
System.out.println("Entering "+method.getName());
method.addLocalVariable("elapsedTime", CtClass.longType);
method.insertBefore("elapsedTime = System.currentTimeMillis();");
method.insertAfter("{elapsedTime = System.currentTimeMillis() - elapsedTime;"
+ "System.out.println(\"Method Executed in ms: \" + elapsedTime);}");
}
return cc.toBytecode();
} catch (Exception ex) {
return null;
}
}
});
}
}
Note that returning null is preferred to returning the original array if you didn’t change anything as then the JVM can immediately recognize that you didn’t change anything, without looking into the array contents.
I am trying to write a code that compiles and runs another java class, after it creates it from a String.
My problem is when I run
Class classToLoad = null;
ClassLoader classLoader = Server.class.getClassLoader();
try {
classToLoad = classLoader.loadClass(className);
} catch (Exception e) {
e.printStackTrace();
}
It throws a ClassNotFoundException. My problem isn't about the package, because if I debug the code and place a breakpoint before the "getClassLoader" and I reload the classes, then my code works fine and it sees the class that was recently created earlier in the app.
How can I reload the classes during runtime so the loadClass will work?
Take a look at this tutorial:
ClassLoader Load / Reload Example
... Let's look at a simple
example. Below is an example of a simple ClassLoader subclass. Notice
how it delegates class loading to its parent except for the one class
it is intended to be able to reload. If the loading of this class is
delegated to the parent class loader, it cannot be reloaded later.
Remember, a class can only be loaded once by the same ClassLoader
instance.
As said earlier, this is just an example that serves to show you the
basics of a ClassLoader's behaviour. It is not a production ready
template for your own class loaders. Your own class loaders should
probably not be limited to a single class, but a collection of classes
that you know you will need to reload. In addition, you should
probably not hardcode the class paths either.
public class MyClassLoader extends ClassLoader{
public MyClassLoader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String name) throws ClassNotFoundException {
if(!"reflection.MyObject".equals(name))
return super.loadClass(name);
try {
String url = "file:C:/data/projects/tutorials/web/WEB-INF/" +
"classes/reflection/MyObject.class";
URL myUrl = new URL(url);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass("reflection.MyObject",
classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
Below is an example use of the MyClassLoader.
public static void main(String[] args) throws
ClassNotFoundException,
IllegalAccessException,
InstantiationException {
ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();
MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
Class myObjectClass = classLoader.loadClass("reflection.MyObject");
AnInterface2 object1 =
(AnInterface2) myObjectClass.newInstance();
MyObjectSuperClass object2 =
(MyObjectSuperClass) myObjectClass.newInstance();
//create new class loader so classes can be reloaded.
classLoader = new MyClassLoader(parentClassLoader);
myObjectClass = classLoader.loadClass("reflection.MyObject");
object1 = (AnInterface2) myObjectClass.newInstance();
object2 = (MyObjectSuperClass) myObjectClass.newInstance();
}
Probably asking: "What is the context in which you are loading the class?" will help answer your question better.
Most standard frameworks like Spring handle loading classes internally and exposing only the methods that those classes provide.
Try Class.forName(String name) to attempt to load the class and return the handle to the class object.
If you want to specifically use your own classloader to load the class, use the overloaded: Class.forName(String name, boolean initialize, ClassLoader loader)
But you will need to ensure that your classloader is able to locate the class to load correctly.
For the classloader you are using, try:
Thread.currentThread().getContextClassLoader()
I'm really newbie to groovy scripting but following some tutorial I tried to dynamically load some groovy class within my java code using parseClass() method of GroovyClassLoader.
I wrote some snippet and it worked fine for me. The problem is that I don't clearly understand what groovy engine is doing beyond my view and how those scripts are compiled?
Does a new class gets creted and loaded into jvm? Or does my application uses some cached sources?
Here is the class I'm trying to parse:
private static class MyScript {
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("public class SomeClass\n");
builder.append("{\n");
builder.append("Some code...").append("\n");
builder.append("}\n");
return builder.toString();
}
The I load it with build() as below:
private Class MyGroovyBuilder {
private Script script = new Script();
public String build() throws TemplateCompilationException
//
String groovyText = script.toString();
//
CompilerConfiguration config = new CompilerConfiguration();
//
byte[] bytes;
try
{
bytes = groovyText.getBytes(config.getSourceEncoding());
}
catch (UnsupportedEncodingException e)
{
throw new TemplateCompilationException(e, groovyText);
}
//
InputStream in = new ByteArrayInputStream(bytes);
GroovyCodeSource gcs = new GroovyCodeSource(in, "SomeName", "/groovy/shell");
GroovyClassLoader loader = new
GroovyClassLoader(Thread.currentThread().getContextClassLoader(), config);
Class<?> scriptClass;
try
{
scriptClass = loader.parseClass(gcs, false);
}
catch (CompilationFailedException e)
{
throw new GroovyCompilationException(e, "SomeName", groovyText);
}
catch (ClassFormatError e)
{
throw new GroovyCompilationException(e, "SomeName", groovyText);
}
return scriptClass.getName();
}
Any clarification is greatelly appreciated.
BR.
After loading class it appears in your class loader, and can be accessed like any other class.
There is a simple tutorial [here], that show how to load class from string.
In simplest case, you can load class, and hold it's Class object, using it to create objects dynamically. For field access or method invokation you can rely on Groovy dynamic nature.
There is no "cached source" or smth like that behind the scene and you can forget, from where your class is loaded. You can also cache classes, that are already compiled, and save them somewhere, as described [here]. It will drastically improve performance, if you need to load same class often.
But it will be better, to dig down in topic, because dynamic class loading is advanced Java/Groovy technique, it's whole infrastructure of chained classloaders, so it's better to refer documentation about them.
Links below may be helpful.
http://javarevisited.blogspot.ru/2012/12/how-classloader-works-in-java.html
How to use URLClassLoader to load a *.class file?