LNCS, vol. In: Bertino, E. ECOOP Kiczales, G. In: Aksit, M. Marek, L. Moret, P. Pearce, D. Sewe, A. In: Watt, D. CC Zheng, Y. In: Furia, C.
This can be easily verified by moving thread sleep call at a later block of demo Java application code, for example, right after new instance of class Text is created:. As seen, Java agent was loaded into JVM, but it was too late for instrumentation to take effect — instrumented class has already been loaded using its original bytecode version.
Variation of this technique usage would be dynamic load of Java agent into running JVM by an application that is executed within the same JVM. Surely, this is not a technique that is commonly used in day to day activities of PI developer or NetWeaver technology consultant, but it is worth being aware of it.
Though I am still half way through reading and understanding your blog but couldn't hold myself back to let you know that it is a really well written blog.
Question: When I check documentation on Instrumentation package I see that they have mentioned that we cannot add a field to a class-. But then when I read javassist documentation, they talk about adding a field in the class. I did try the steps they have mentioned to add a field but I keep getting UnsupportedOperationException. I'm glad you got interested in this topic.
Regarding the error, can you provide more details and context of that error? For example, code snippet that you use, and text of the exception that you get?
I would suggest checking if modification is allowed in your environment in the JVM. Instrumentation API has two flavors of modification operations that can be applied to the bytecode - redefinition and retransformation, and there are corresponding checker methods that can be used to verify if modification is allowed Instrumentation.
Skip to Content. Vadim Klimov. March 9, 21 minute read. Intro In this blog, I would like to describe one of techniques that can be used to flexibly change application logic executed by a Java application server — or, to be more precise, within Java Virtual Machine JVM of its server nodes. Several remarks regarding presented demo applications shall be made upfront: In order to avoid irrelevant complexity, examples are based on a standalone Java application.
Since the described capability is a part of JVM features and is not specific to a particular application server implementation, it can be adopted and used in real life scenarios in conjunction with various application servers SAP Application Server Java being one of them ; All development is simplified, so that amount of code lines is reduced to reasonable minimum and we may stay focused on core subject, even though resulting in heavy usage of hard coded values and simplistic class model design.
In real life development, majority of hard coded values shall be exposed as configurable parameters; In a standalone program and complementary developed classes, output to console is used intensively to make it transparent and informative, when corresponding objects are called and what their state is.
In order to make segregation of functionality used in demonstrations more obvious, developed classes are located in following packages: Java application that we are going to break into and instrument, is located in a package vadim. Correspondingly, instrumented Java application, Java agent and Java agent loader are located in three different Java projects, leading to following projects structure: I will start from a basic application and will gradually enhance implemented features in order to illustrate various practical aspects of discussed topics and techniques, so used projects and their content will change over time across this blog.
Class DemoApplication implements method main and is an entry point for the called Java program: package vadim. Bytecode instrumentation and manipulation Starting from Java 5, JDK provides developers with functionality of so-called instrumentation of bytecode. Luckily, there are several libraries that simplify operations with bytecode — below are several most commonly used of them, split by level of abstraction from the generated bytecode: Level of abstraction from bytecode Description Examples Low level Libraries require manipulation directly on bytecode level.
Commonly they provide most feature-rich capabilities, but are also most complex in usage in comparison to other bytecode manipulation tools. Provided example incorporates several different instrumentation activities and illustrates how we can achieve following modifications: Insert extra code before execution of a given method of an instrumented class; Insert extra code after execution of a given method of an instrumented class; Inject extra code in the middle of a given method of an instrumented class; M odify existing code of a given method of an instrumented class.
Few key points to be taken into consideration: Javassist provides capability to access compile time class definition which is rendered version of its bytecode ; It is then possible to iterate through class methods or access method by its name and descriptor.
Please pay attention to notation of method descriptor — it corresponds to bytecode compatible notation rather than the one defined in Java language specification; For a given method, it is possible to insert arbitrary code before or after method, or inject code at a given code line. Please pay attention to syntax — injected code lines are Java-like strings with some modifications like proper escaping of some special characters, possible usage of placeholders, etc.
Here right before call of System. Class DemoApplication is enhanced correspondingly — instrumentation of bytecode is implemented in method enableInstrumentation : package vadim. CannotCompileException; import javassist. ClassPool; import javassist. CtClass; import javassist. CtMethod; import javassist. NotFoundException; import javassist. DemoExpressionEditor contains implementation of logic that is further used to instrument called method: package vadim.
ExprEditor; import javassist. Java Agent and Attach API Up to now, we got familiar with some fundamentals of bytecode instrumentation, but example provided above is still not flexible too much — we needed to embed extra logic into an application or would have needed to deploy some other application that would instrument required classes bytecode.
Advantage of this approach is that agent code is loaded before JVM calls method main of a started Java application. Loading instrumentation before instrumented application guarantees that a Java application is instrumented during its complete runtime lifecycle within JVM. Corresponding API implementation is located in library tools.
I will only highlight some major key points: Java agent main class has to implement corresponding methods that will be invoked during agent startup: method premain for agents starting during JVM startup, method agentmain for dynamically loaded agents. Class DemoAgent contains implementation of methods premain and agentmain : package vadim. ByteArrayInputStream; import java.
IOException; import java. ClassFileTransformer; import java. IllegalClassFormatException; import java. Code coverage tools may use this technique to inject tracing calls into the tested code, and then analyze the collected data in order to generate testing coverage reports. Frameworks may use instrumentation to implement some of their services.
It may seem like a wild idea at first — an application that modifies itself during runtime? Sounds like a script for a sci-fi movie! Java is a dynamically loaded language. Every class is loaded lazily when needed using a component called a class loader. The class loader looks for the class code, reads it, and passes it to the JVM which links it with the rest of the code that is already loaded. This behavior makes Java very flexible — selection of concrete classes is deferred to the last possible moment, and the code itself can be brought from exotic places of all sorts [ 4 ].
Classes are stored on the disk in. The compiled code itself is kept in bytecode format — an intermediate language, which resembles assembly language. Normally, the class loader just reads the class file from wherever it is stored and passes it to the JVM as an array of bytes, but since it is just a structure in a well-defined and rather simple format, we can reason about the code and do some sophisticated things with it prior to that.
In our case, the code is modified at that stage, right before the class is defined by the JVM. For the bytecode modification itself, you can use open source frameworks, like ASM and BCEL which is somewhat outdated that take care of some of the more annoying technical details involved in this process. And this is bytecode instrumentation in a nutshell — seems magical at first, but really simple once you realize how it is done. Net applications, although the process differs from what I describe in this post [ 2 ] Application Performance Management [ 3 ] I like to call it pointing fingers scientifically [ 4 ] Network locations, dubious coding sweatshops, whatever….
Also, I feel obliged to note that programs that change their own code are rather… old, and more common that you might think.
0コメント