I'm working on a small project that determines code coverage when testing a java application. It basically consists of a plugin for an IDE which finds all the classes and methods in the project and saves them in a database, and an agent with aspectJ pointcuts that weave around all of these methods to log their execution.
The problem I have is that I only want to log the methods that are actually written by the developers of that very project and not those of underlying libraries. So the pointcuts need to be defined in a way that only methods of classes in the actual project packages are woven. On the other hand, since the agent is to be used with all sorts of projects, I can't hardcode those packages.
My attempt so far was to read all the package names from the database and build a string from that. Basically what it looks like is this:
private static final String POINTCUT_STRING = AspectUtil.buildPointcutString();
And then, when defining the pointcut:
#Pointcut(POINTCUT_STRING)
Thing is, this doesn't work because apparently when defining a Pointcut, the
Attribute value needs to be a constant.
So, how can I make it so that i can only weave methods in classes in the packages that I have in my database?
Thanks in advance, have a good one!
I don't think a dynamic aspect approach is going to work as aspectj does not expose the weaver to any state management or changes. Although this would be theoretically possible at runtime it's definitely not possible at compile time (and you have the option to add your aspects at compile time).
But to your issue...
What weave strategy are you using? compile or runtime? I've found compile to work very well and I'm not sure how to use runtime with aspectj. But what I can say is that if you use compile you'll only be weaving the application classes in any case as that is all you'll have access to.
Another comment to make is if you want to do something dynamic you'd be better off putting the condition on whether to monitor that method for code coverage downstream of the aspect. So when the aspect is executed the first thing it will do is decide if this class/method call should be monitored for coverage and then go on from there...
When I asked you:
What do you mean by "runtime weaving"? Load-time weaving (LTW) maybe? I.e. you are using aop.xml? I am asking for a specific reason.
You replied:
Yes, LTW. I am using an aop.xml file.
In that case you have the option of specifying pointcut definitions in aop.xml which is read during JVM start-up when the weaving agent is activated. For refererence, please read the AspectJ Developers Guide, there is a chapter on LTW. You will find sample code and sample XML definitions there, showing how you can extend an abstract aspect with an abstract pointcut in the XML file and specify a concrete pointut for a concrete subclass. That should give you the options you need to keep your pointcut out of the Java code, for whatever reason you think that's a good thing and you need it.
Please note that you cannot expect to modify aop.xml during runtime and re-load it, possibly re-applying the aspect dynamically to all classes. AspectJ LTW works in connection with class-loading, i.e. you only have one shot at JVM start-up before all application classes are loaded. This is not an AspectJ limitation but just how bytecode instrumentation in the JVM works.
Related
I would like to verify that the code has been woven by aspectJ. One of the methods I've heard of is using agents from the byte-buddy library. Unfortunately, I am completely green in it and I do not know what I could do.
I tried to use JADE with agents, but byte-buddy is more friendly for myself and I think it's more suitable.
To verify the method I created a simple springboot application with MySQL connection and added some aspectJ code. I tried with Spring AOP, but AOP cannot read private methods, so I decided to provide my idea with aspectJ.
I already used another aspect to define joinPoints and throw an exception, but it's hard and takes a lot of works with the low results.
Do u have any ideas about methods or implementation of an agent with byte-buddy to detect aspectJ?
First an answer to your comment:
I wanted to have more experience with untrusted code. For example if I get a big project in Java and then I get some untrusted plugin with changes created by AspectJ, then I prefer to know what's to do to inspect that my classes are secured from AspectJ or that the AspectJ just weaves into my classes.
For AspectJ there are two scenarios:
Compile-time weaving
Your application does not use AspectJ, no AspectJ Maven plugin in your Maven build or something similar in your Gradle build: Even if your third-party library is on the classpath, nothing gets woven into your own code. How could it? There is no AspectJ compiler.
Your application uses the AspectJ compiler: Even if the third-party library is on the classpath, no aspects are woven into your own code because you would have to explicitly put the library on the aspect path in order to achieve that. BTW, if the third-party library is aspect-enhanced, it will have a dependency on the AspectJ runtime aspectjrt.jar, otherwise it would not work. Even if the runtime was shaded into that other JAR, still nothing would happen to your own code.
Load-time weaving
Your application does not use LTW: Obviously nothing would happen, no aspects from the other JAR could be woven into your code without you starting the JVM with an active weaving agent.
Your application uses LTW: You would need to provide an aop.xml file and there you can explicitly list aspects you want to be applied, which target classes or packages to include into or exclude from weaving, turn on -showWeaveInfo in order too see on the console which aspects are woven into which joinpoints. There is one caveat, though: If the AspectJ weaver finds more than one aop.xml or aop-ajc.xml files on the classpath, it will conceptually merge them. I.e. if the third-party library comes with such a file, it would be merged into your own as explained in the AspectJ development guide (search for "several configuration files" on the page). So this could lead to some undesired side effects. The simplest thing to do if you want to inspect your third-party libraries is to scan the JARs for the presence of aop.xml or aop-ajc.xml, respectively. Then you can look at the content of those files, if any, and easily assess their possible effect on your code. But just checking the output of -showWeaveInfo on application start-up also reveals if unwanted aspects are woven into your own code.
So actually you don't need to scan for anything inside class files in order to detect AspectJ markers, but if you absolutely wish to here is something you can do:
Most AspectJ-woven classes contain fields or methods (can be static or non-static, private or public) with ajc$ somewhere in their name, usually at the beginning. That's just a heuristic approach, but hey - whatever works, right?
If I say "most classes" I mean that in some cases if an aspect only uses ITD in order to make a class implement an interface, introduce new methods or public fields, the class would just get the new elements but no AspectJ-specific code. But then they would just be normal classes, there is no cross-cutting advice code or whatever which could affect your own application.
If you would find such members or fields, it would indicate that you either found classes enhanced by aspects (which could not affect your own classes even in the LTW scenario with extra aop.xml) or aspects themselves because they also contain the same markers plus some other stuff you could scan for.
So how can you scan those third-party class files?
Command line: javap -p path/to/MyClass.class | grep 'ajc[$]'
Java code to scan classes already loaded into your (test or inspection) application:
package de.scrum_master.aspect;
import java.lang.reflect.Member;
import java.util.stream.Stream;
public class AspectJDetector {
public static boolean hasAspectJMarker(Class<?> clazz) {
return Stream.concat(
Stream.of((Member[]) clazz.getDeclaredMethods()),
Stream.of((Member[]) clazz.getDeclaredFields())
)
.filter(member -> member.getName().contains("ajc$"))
.findFirst()
.isPresent();
}
}
Just call hasAspectJMarker(MyClassOfInterest.class) for each class you want to check. If you find AspectJ-enhanced classes, there is again no need to worry. Only if you find aspects there might be. But as I said above: This would be easier to detect via aop.xml in that JAR. The XML file would even list the aspects explicitly via <aspect name="com.MyAspect"/>, basically a no-brainer.
Is there a way to modify .class files in order to add Java annotations to certain methods? Basically I want to traverse methods of each class file in a jar file and annotate certain ones. Note that this is not at run-time while using the jar file. Rather, after I'm done I want to have modified class files with the annotations.
I do have access to the source code, so if there's an automatic source code modifier, that would work as well...
I'm assuming I'll need a tool such as Javassist or ASM. If so, which one should I use and how would I go about it?
Actually, this is a classic use case for AspectJ:
declare #method : public * BankAccount+.*(..) : #Secured(role="supervisor")
While I will grant you that direct byte code manipulation is more powerful, AspectJ is much more user-friendly, and it immediately gives you compiler warnings when you are doing something wrong.
Also, if you use Load Time Weaving, you can leave the original library jar unchanged, because the weaving happens at class-load time.
Reference:
Declare Annotation
AspectJ in Action (book)
Googling for an hour or so turned this article up which seems to completely answer my question: use ASM. To write class files using the changed bytecode, use ClassWriter.
Well, time to get to work then, I guess. :)
Is there a way to perform (more or less) "automatic" JSR-303 java bean validation without runtime modification of a class?
Usually I see people using AspectJ to accomplish this, but we have had so many complications when using runtime code weaving (like with cofoja) that I'd like to avoid it. It makes a lot of our build tools fail because the runtime class files were different than the class files on disk.
I've looked at dynamic proxies via reflection which can only proxy interfaces (public methods) AND if you call anything annotated inside "this", you don't go through the proxy anymore so you lose that validation.
I'v also looked at ByteBuddy for a way to possibly intercept method calls by wrapping/redefining a class. There might be something here but I can't figure out how to intercept private methods or accomplish the above without getting back into modifying the original class.
Any ideas?
In theory, you can enforce bean validation by reflection only. But I assume that by automatic, you mean without an explicit call to a validation method.
In this case, instrumentation is probably your only option. With Byte Buddy, you can instrument existing methods, by using redefinition or rebasing. The easiest way to do so is a Java agent using an agent builder or using the Gradle or Maven plugins for build time instrumentation. The documentation provides an example of how to implement an agent and the build instrumentations have a lot of javadoc (documentation in progress).
I'm implementing an audit api to log any method invocation which has an #Auditable annotation in its declaration. The basic requirements are that it must be used by current applications and should be non-intrusive. I already have the auditing API by means of a Log4J2 wrapper, and also I successfully used it in some EJB Interceptors. However, I need to create an all-terrain interceptor I can use wherever the annotation is used, i.e., I'd like to annotate servlets, EJBs and POJOs methods with it and let the interceptor work its magic.
I've tried Java EE Interceptors and they only work in EJB, I've tried GUICE but it doesn't work with servlet nor EJB methods, only guice injected POJOs.
I'd like to know, if somebody knows how, what should I use and I'll be gratefull if an example can be pointed at to.
Thank you very much.
One possible solution is using AspectJ. AspectJ can let you define what you want to intercept (the so called joinpoint) and execute the code you want (the so called advice) when it happens.
Joinpoints can be everything, but in your case you need to trap the execution of methods with a given annotation, so we will be using only the "execution" joinpoint.
AspectJ then needs to instrument your classes. This can be done :
Compiling your code with the AspectJ compiler, which extends the java compiler with what needed
"Weaving" existing jar files, so you can do it after the normal build process, or you can do it even on jar files you don't have the sources (which is not in this case, since you need to place the annotation anyway).
We will use the first method only to compile the aspect, and the second one to instrument the jar files or class folders you want.
To do it, you need to write an annotation, Auditable, that must have runtime retention.
Then you write an aspect :
public aspect Audit { // An aspect then compiles as if it was a class (at runtime, it is a normal class)
Object around() : // This is an around advice
execution(#Auditable * *(..)) // Here you're catching execution of any method of any class with any parameter and any return type, which is annotated with #Auditable
{
// Do what you want to do before the method
long start = System.nanoTime();
Logger.log("I'm entering into " + thisJoinPoint); // with thisJoinPoint you can extract a lot of informations, like what class, what method, what parameters, on which instance etc..
try {
Object ret = proceed(); // "proceed" goes on executing the method
// You could log, or do whatever you want with the return value, which could be "Void".
return ret;
} finally {
Logger.log("It took ns: " + (System.nanoTime() - start));
}
}
}
Once you wrote this aspect (usually in a package and with a ".aj" extension, but is not mandatory), you compile it with the AspectJ compiler.
ajc -outxml -classpath thisLib.jar:thatLib.jar -1.5 -outjar auditableAspect.jar package/of/your/AuditableAspect.aj
This is similar to compiling java code with javac. On the classpath you need what is needed to compile the aspect, in this example i used a misterious Logger, which must be inside thisLis.jar or thatLib.jar.
At the end, you'll have an auditableAspect.jar file, that will contain the compiled class of you aspect.
Now, to use it, you need to "weave" the classes (or jar files) you want it to "intercept". To do this, still with the ajc command :
ajc -inpath myStuff.jar -outjar myStuffAudited.jar -aspectPath auditableAspect.jar
You'll then find in myStuffAudited.jar the same classes in myStuff.jar, and a few other synthetic inner anonymous classes.
Now basically you can remove from your application classpath (or WEB-INF/lib if it's a web application) the myStuff.jar, replace it with myStuffAudited.jar, and also add auditableAspect.jar which is your aspect and aspectjrt.jar which is AspectJ runtime.
Reload you application, and it will start logging.
You can do this on all the jars you want, or on all jars in a for loop; those unaffected will remain unaffected.
This can be integrated in a number of ways in your build cycle.
Obviously, a .bat or .sh script can automate it
Ant has tasks for aspectJ
Maven has a plugin for aspectJ
Eclipse has a plugin for compiling and weaving inside the ide
Don't know how you build your project, so YMMV.
Wether this is intrusive or not, it's an opinion; but to my knowledge there aren't a lot of packages out there that offer runtime auditing without somehow manipulating the class bytecode, if not using runtime proxies that are limited (as you have seen) to interfaces.
There is also an option to use a weaving classloader, that would remove totally the need to weave existing jars and classes, cause they would be weaved at load time by the classloader itself. It works great and i like it, but it depends on your runtime environment.
I want to use AspectJ to monitor database statements.
When I define the pointcut as
#Pointcut("execution(* java.sql.*Statement.execute*(..))")
it does not work. But when I define the pointcut as
#Pointcut("execution(* *.*(..))")
it can monitor all methods.
How can I define the pointcut to monitor database access methods only?
What you say is not true, because with the second pointcut you can capture a whole lot of method executions but nothing inside the JDK. If you think about it for a while you understand why: AspectJ instruments byte code, but only the byte code of your own classes or libraries, not the JDK byte code because you do not weave your aspects into the JDK. Thus, you cannot capture any execution() joinpoints inside the JDK (except if you instrument the class files inside rt.jar or so).
Now how do you solve your problem? First you need to understand the fundamental difference between execution() and call() pointcuts: The former is woven into callees, i.e. right into the target methods. The latter is woven into callers, i.e. into all places where the target method is called.
So you want to try
#Pointcut("call(* java.sql.*Statement.execute*(..))")
But there is a caveat: You are fine if your own code, i.e. the one compiled with AspectJ, calls the statements directly. If you are interested in capturing calls made by 3rd party libraries as well, you either need to weave those as well via binary weaving (producing a new library with woven class files replacing the original one) or via load-time weaving (instrumenting the byte code while it is loaded during runtime). Both is possible. But how to do that is beyond the scope of this article, please read the AspectJ documentation for instructions. Last but not least, if JDK code calls those methods internally, you will not be able to capture the calls by any other means than also weaving the JDK (rt.jar or wherever the SQL code resides), as mentioned above.
I know this is a complex answer to a simple question, but you really need to understand AspectJ before you use it, otherwise you might not fully grasp what I was trying to explain. AspectJ is fun to learn and experiment with, take your time and enjoy it. If you just copy and paste code snippets you are but a fool with a (very powerful) tool. :-)