How can I access a RetentionPolicy.CLASS java annotation? - java

Is there a way to configure a ClassLoader or a JVM to load annotations with CLASS retention policy, so I can access them using reflection?
This is useful for compile-time post-processing, as mentioned here.
I annotate some of my classes in order to generate an antlib.xml file automatically. I would prefer if my annotation could have CLASS retention policy, so that it does not create runtime dependencies.

javac can process source and class level annotations, with the -processor option. See javax.annotation.processing.AbstractProcessor. (Since java 1.6).
I started using it while compiling .java files. Apparently it can also be used to process CLASS annotations with .class input files. I haven't tried this because I'm using ant to compile, and ant does not seem to pass .class files to the compiler.
I have to do a full compile when I want to process all the annotations in my project.

I think you might want to have a look at this tutorial.
It explains how to create your own annotation processor and how to use it to generate code. It doesn't handle bytecode manipulation though.
He also gave a presentation available on YouTube. In case you're too lazy to read... ;-)

Related

Files with the .SCL.lombok extension

When the lombok jar file is opened in Intellij, all files other than the annotations end with .SCL.lombok (e.g. HandleAccessors.SCL.lombok). I was just wondering what the reason for this was and how it's handled.
The reason for it
Lombok has a public API - the stuff you're supposed to interact with. That'd be, for example, the #lombok.Getter annotation. Those are just class files in that jar, the aim is simply: add that jar to your classpath and your IDE autocomplete dialogs and the like will automatically start suggesting these, as per design.
But, lombok also has lots of classes that just 'make it tick', these aren't meant for public consumption. Things like lombok.eclipse.HandleGetter, which is the implementation for handling the #Getter annotation inside the eclipse agent. There is no point or purpose to referring to this class anywhere, in any project - it's an internal lombok thing. If we just stuck that jar file into the jar, and you typed Handle and hit your IDE's autocomplete shortcut key, you'd still get the suggestion.
Similarly, we ship a few dependencies straight into lombok.jar - it's a 'shaded jar' (a jar with all deps included), though we don't have many, keeping lombok.jar a nice small size. Still, ASM (a bytecode manipulation library) is in it, and that is fairly popular.
The standard shading solution offered by most shading tools is to prefix something to the name. ASM's org.objectweb.asm.AnnotationVisitor class would become org.projectlombok.shading.org.objectweb.asm.AnnotationVisitor. Point is, your IDE doesn't know that, and if you ALSO use asm in your project (where you also use lombok), and you want AnnotationVisitor thus you type AnnV and hit cmd+space or whatnot, your IDE suggests both. That's ugly and we'd like to avoid this.
Hence, we built our own shader, and it works by not having class files in the first place. This way, IDEs and any other automated tool doesn't even know either our ASM classes, or our implementation details, even exists. The only files that such tools (such as your IDE) sees are the types you're meant to see: lombok.Builder, lombok.extern.slf4j.Slf4j, lombok.experimental.UtilityClass, etcetera.
How does it work
Java's classloader architecture is abstracted: You can make your own. The primitives offered by a class loader is simply this: "Convert this byte array containing bytecode (i.e. the contents of a class file) into a Class<?> definition", and the primitives that you're supposed to implement when you write your own classloader is twofold:
Here is a resource key, such as "/com/foo/load.png". Please provide me an InputStream with this data.
Here is a fully qualified class name, such as "com.foo.MyApp". Please provide me with a Class<?> instance representing it.
Out of the box, java ships with a default classloader. This default classloader answers these questions by checking your CLASSPATH - which can be provided in various ways (via the jar manifest's Class-Path entry, or via the -cp argument to the JVM executable, or the CLASSPATH environment variable), and scanning each entry on the classpath for the resource requested, capable of reading the file system as well as opening jar files.
But that's just a classloader. One implementation of the general principle that's baked into java. You can write your own. You can write a classloader that generates resources on the fly, or that loads them from a network.
Or, as lombok does, that loads them by opening its own jar and looking for .SCL.lombok files.
Thus, lombok works like this: When you launch it, the 'entrypoint' (the class containing public static void main - or in lombok's case, for javac mode it's the annotation processor entrypoint and for eclipse it's agentmain), we 'hide' it from you using some fancy trickery: agentmain does not need to be in a public class (it can't be .SCL.lombok files - our classloader isn't available yet, we need to bootstrap that up first!). annotation processors do have to be in a public class, but, it's a public class inside a package private class, thus, just about every IDE knows it's 'invisible' and won't show it, but javac's annotation runner accepts it.
From there, we register a classloader that is capable of loading classes by way of reading in an .SCL.lombok file, and this lets us hide everything else we want to hide.
I want to develop lombok and this is getting in the way!
No need; just clone our repo, run ant eclipse or ant intellij, and off you go. There is no way to extend lombok without first forking it; we'd like lombok to be able to be extensible without it, but that would be far more complicated than simply not doing the .SCL.lombok thing. Eclipse runs on top of equinox, a runtime modularization system, and making that work properly requires all sorts of stuff that would make 'just toss some extra handlers on the classpath' not a feasible route to extending lombok in the first place.

How can I run lombok features in my executable jar file?

Consider I have a constructor of more that 10 or more args, and I am using it in criteria select query to fetch data.
Now the problem is that I want to remove my constructor as it gives me sonar issues.
So I tried to Lombok dependency, #AllArgsConstructor it is working in my IDE,
but when I create the jar file it is not being used by JPA query.
I need help either to make lombok work in my project.jar file,
OR
change in select query which does not use constructor,
OR
Any other better way
Lombok is a compile-time affair. Once your code is compiled (you have class files), lombok shouldn't be there / wouldn't be doing anything. Lombok is not a library in that sense. You don't ship 'javac' with your app either.
If lombok is part of the compilation, then lombok will do its thing. If it's not, then your compiler would generate an error; after all, it wouldn't know what #AllArgsConstructor is about. So, if you're not observing any compilation errors, lombok did its thing*.
Secondly, sonar is a linting tool. It's there to help you out. If it is 'telling you off' for writing code where you think it is the best way to do it, then tell sonar to stop complaining about it. It's a tool for you to use; not a prison.
You can use javap to check if the all-args constructor is there. If it is (it should be), then your question is actually: How do I make my JPA tooling use that constructor?
*) It is possible, but takes some work, to tell javac to have lombok's classes available, but NOT to run it as an annotation processor. But if you do manage that, then lombok wouldn't have transformed any of your classes. I assume that's not the case, as you need to go out of your way with lots of command line switches to make this happen.

Process annotations from java files without compiling

First maven project contains the sources with annotated classes.
Second maven project contains the annotation processor (javax.annotation.processing.AbstractProcessor).
I would like second project, on compile time, to process the sources (annotated) of first project and do some stuff.
How should I approach it?
I am guessing Annotation Processor is not the right choice as it required to be bounded to a compiler...
The other option is to scan all java files in first project, load them (with class.forname) and process the annotation.
Can you suggest something else?
You can supply the -proc:only command-line argument to avoid compilation -- no .class files will be output.
The javac documentation says:
-proc: [none, only]
Controls whether annotation processing and compilation are done.
-proc:none means that compilation takes place without annotation processing. -proc:only means that only annotation processing is done,
without any subsequent compilation.

Could java reflection add method into class file that are not in java source file?

I'm digging into the source code of the deeplearning for java recently. There is such a class NeuralNetConfiguration in which there are tons of fields that all requires getters and setters. The NeuralNetConfiguration.java source code does not provide any, however.
When I open this project in IntelliJ, ctrl click on the usage of this class, which are methods mostly like, NeuralNetConfiguration.getNInput() or NeuralNetConfiguration.getKernelSize(), the IDE direct me to the compiled class file in which all the getters are defined for each of the field in this class.
Just wonder how this is done since I'm a new bee to java. Posts I found about java reflect suggest that reflect can not add method to a method to a class unless you wrote your own classloader. I check the deep learning for java project and I don't think they have done that.
What bothers me too from time to time is, IntelliJ starts to report errors that those getFields methods could not be resolved since they are not in the source file at all, especially after my building the project using IntelliJ instead of using mvn command line.
The magic happens with the #Data annotation on the class. This annotation is from Project Lombok. There is probably an annotation processor somewhere that hooks into the compiling process and generates these methods.

Java runtime retention annotations - annotation class required at compile time but not at runtime?

I am trying to figure out the exact effects in regards to compile time and runtime classpaths of a class annotated with a runtime level retention annotation, for example: #javax.inject.Named. This is my understanding so far:
The javax.inject.Named class must be present at compile time on the classpath.
This is then compiled into the bytecode as meta-data and can be obtained at runtime via the Reflections API and processed accordingly; however, this class DOES NOT need to exist on the runtime classpath.
Thus, if I compile a jar with a java class annotated with #javax.inject.Named, this compiled jar file can be run both in a container (ie Jboss) and in a plain old JVM launched from the command line (with no additional classpath entires). However, if I make this source code available to a project that will be compiled along with it just using javac (and no additional classpath entires), it will no longer compile correctly? If this assumption is correct, this seems to be a little limiting if I need to share both compiled binaries and source files amongst projects that may or may not be running in a container (ie Jboss).
If this is the case, would I need to create a wrapper object (annotated with #javax.inject.Named) that delegates to the inner, non-annotated object? Is another option to disable annotation processing at compile time? See http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html#processing
You need the annotation to be present in the compile classpath to compile source files using the annotation. You don't need it in the classpath to compile a class A using a compiled class B using the annotation.
Many open-source libraries (like Guava or DbSetup for example) use the javax.annotation or Findbugs annotations for example, but you don't need to have those annotations in the classpath to compile (and run) your own source code relying on these libraries.

Categories