How to implement build specific annotation retention in Java - java

I have an annotation that I currently use only for internal build and documentation purposes. It does not offer any value at runtime, which is why I chose #Retention(SOURCE):
#Retention(SOURCE)
public #interface X
However, in order to validate its proper usage, I would like to implement a unit test that navigates the entire API to check whether the annotation is applied everywhere it should be applied to. That unit test would be quite easy to implement by using ordinary Java reflection APIs, but I cannot do that as the tests can't reflect over the annotation, given its #Retention(SOURCE).
In order to use reflection in tests, I would have to change it to #Retention(RUNTIME), which I would like to avoid due to the overhead in byte code at run time.
Workarounds I'm aware of:
There are workarounds as always. I'm aware of these:
We could use an annotation processor that fails the build instead of running unit tests. This is feasible but less optimal, as the tests are quite sophisticated and much more difficult to implement using annotation processors rather than unit tests using both junit APIs and the much more convenient reflection API. I would like to use this workaround as a last resort only.
We could change the #Retention to RUNTIME in our sources, build the sources with these additional tests, then pre-process the API to remove the retention again, and then build the API a second time for production usage. This is an annoying workaround as it would complicate and slow down the build.
Question:
Is there a more convenient way to retain the annotation at runtime only for tests, but not in the actually built jar file, using Maven?

Here's a hybrid approach that might work.
Write an annotation processor that doesn't implement the full testing that you want to do, but instead merely records in a sidecar file where the annotations occurred. If you're annotating classes, methods, and fields, the location can be recorded fairly straightforwardly using the package-qualified class name plus a method or field descriptor. (This may be more difficult, though, if your annotation can appear in more obscure places such as on method parameters or at type use sites.) Then, you can keep the retention policy as SOURCE.
Next, write your junit tests to do whatever reflective analysis you're intending to do. Instead of trying to find the annotations reflectively, though (since they won't be there) read in the sidecar file and look there.

I think you covered the solution space pretty well.
Two more you didn't cover:
Strip the annotation later in a post processing step using a tool like proguard.
Hack your compiler to switch the annotation retention depending on a flag. Pretty sure you can switch some flag in the internal meta data. Maybe injected by another annotation processor triggered by the annotation #DynamicRetention("flag")?

One of other workarounds may include:
Leaving default retention = CLASS.
Using a library which will read bytecode directly.
#interface X {
}
#X
public class Main {
public static void main(String[] args) throws IOException {
ClassPathResource classResource = new ClassPathResource("com/caco3/annotations/Main.class");
try (InputStream is = classResource.getInputStream()) {
ClassReader classReader = new ClassReader(is);
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(Main.class.getClassLoader());
classReader.accept(visitor, 0);
System.out.println(visitor.getAnnotationTypes());
}
}
}
yields:
[com.caco3.annotations.X]
The library used is ASM:
ASM is an all purpose Java bytecode manipulation and analysis framework
This code uses some classes from Spring Framework:
ClassPathResource - something similar to java.io.File
AnnotationMetadataReadingVisitor (source code) - is a ClassVisitor collecting annotation metadata
However this approach suffers from the same drawback as you described:
overhead in byte code at run time
because (from javadoc):
Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
public static void main(String[] args) throws IOException {
X x = AnnotationUtils.findAnnotation(Main.class, X.class);
System.out.println(x);
}
outputs: null

If #Retention(CLASS) is acceptable, then I would recommend to use ArchUnit. The task you describe sounds like it is a good fit. ArchUnit can be used to define and validate rules for your architecture. For example it can be used to restrict access between certain classes/packages, or e.g. to validate class hierarchies, type names - or annotations.
It is usually executed as a unit test by JUnit or any other test framework. It works by analyzing byte code, so there is no need to switch to runtime retention.
The fluent API is nice and in my opinion way more readable than using reflection or annotation processing for this use case. For example to ensure that certain classes should always have a particular Annotation you would write this rule in a unit test:
classes().that().areAssignableTo(MyService.class).should().beAnnotatedWith(MyAnnotation.class)
It's also possible to create custom rules to assert more complex constraints.

Related

Is there a way to annotate the INSTANCE field of kotlin objects?

I have a Kotlin object that has several fields exposed as static #JvmFields. The parser that I use (which I cannot edit or change) looks for public static fields and creates a configuration file based on those. Since the INSTANCE field is public too, the parser generates a new category called instance. Is there a way to add actual annotations to the INSTANCE field? I would want to add the #Ingore annotation to it so the parser does not use the INSTANCE field.
Basically, the answer is no, Kotlin does not allow annotating or altering the INSTANCE fields in any other way. If you believe this could be a useful feature, please file a feature request at kotl.in/issue.
The valid solutions to this problem are:
Make the bytecode analyzing tool Kotlin-aware, i.e. make it behave correctly with Kotlin declarations. Though this requires non-trivial job to be done and does not seem possible in your case, it could be a valuable time investment.
Create another ad-hoc tool that post-processes the classes produced by the Kotlin compiler and adds the annotations you need, then include that tool into your build.

How to understand annotation in java And How to implement my annotation in java?

What I have known are:
annotation was added in java 5
annotation can be using in method, class, and property
annotation can work in RUNTIME, CLASS, SOURCE( I don't know how to work with CLASS and SOURCE, and their's features)
annotation with retention which is RUNTIME can be implement when java program is running.
And I want to implement a annotation to have follows features:
ensure class only being allowed to create a instance
ensure methods only being allowed to access method in the class
it is like as friend in c++
it is same as public and private , but more dynamicall, like
#MyAnnotation(allowMethods={xxx.doSomething})
public void getValue(){}
the getValues method only can be accessed in the instance self and xxx.doSomething() method
What should I do and learn in next?
And Where can I learn about these?
I think you might be misunderstanding something there. Annotations are descriptive elements, not parts of your program. You can write as many annotations as you want, and people who use your code will still be able to ignore them.
That said, an annotation that enforces a policy (as yours does) can actually be implemented, either at compile or at runtime, but you need an external mechanism to help you. I can think of 3:
Annotation processing lets you interact with the compiler and process annotations by generating code or by omitting compiler errors. Unfortunately, I don't think it will work for your case, as you want to protect your annotated type from instantiation, and that means the call site doesn't actually have an annotation. Annotation processing only gives you access to the actual code pieces that have annotations, not to those that refer to them.
AspectJ allows you to write policy enforcement aspects and omit compiler errors, based on static pointcuts. The problem here is that static pointcuts have very limited semantics, so while you could forbid the instantiation of your class altogether, or from certain packages, you could not limit the your class instantiations to 1.
The third way, and probably the only sane way is that you use a container like Spring or Guice and configure your class as singleton. As long as you only retrieve your class from the container, it will never create a second instance.
Finally: If you want to limit the number of instantiations of your class, you can always use a classic Singleton pattern approach.

Sending events via code generation with annotations

I have a bunch of methods that must send events when called, i.e. something like this
public void someMethod(){
sendEvent("someMethod was called");
// the method does something
}
public void someOtherMethod(){
sendEvent("someOtherMethod was called");
// the method does something
}
I would like to avoid the sendEvent method call by doing something like
#SendsEvent("someMethod was called")
public void someMethod(){
// do something
}
I have heard of annotation processing as a way of generating code at build time. Would this be possible to do? if so could you point me in the right direction (tutorial or docs).
PS: I have searched on the net for tutorials on annotation processing by they all seem to focus on using the reflections API for runtime annotation processing. This is NOT what I want.
I think for this feature, annotation processing is not worth the additional effort and complexity. Annotation processors are often used for code generation but it doesn't seems you need to generate any dynamic code for this use case. You would have to:
Learn the annotation processor and mirror apis (similar to the reflection api but more complex)
Integrate the processor in your build system
Let the processor generate classes that monitor the annotated methods.
How to do this? There are many ways. You could generate a class that extends your class, adds the event call and then executes the original implementation. Other solution probably involve run-time handling of everything and could be done using reflections
Find a way to load the generated classes instead of you own implementations (DI or something)
Unless you actually really need to generate code for this and do everything at compile time, you should probably just do everything at runtime when the application starts. Checking for annotations once in the beginning using reflections should not impact performance in any way. Use a Proxy to intercept method invocations and add your event calls.

How to test "add" in DAO without using "find" etc.?

In following code the issue is, that I cannot test dao.add() without using dao.list().size() and vice versa.
Is this approach normal or incorrect? If incorrect, how can it be improved?
public class ItemDaoTest {
// dao to test
#Autowired private ItemDao dao;
#Test
public void testAdd() {
// issue -> testing ADD but using LIST
int oldSize = dao.list().size();
dao.add(new Item("stuff"));
assertTrue (oldSize < dao.list().size());
}
#Test
public void testFind() {
// issue -> testing FIND but using ADD
Item item = new Item("stuff")
dao.add(item);
assertEquals(item, dao.find(item.getId()));
}
}
I think your test are valid integration tests as stated above, but I would use Add to aid in the testing of of Find and vice verse..
At some level you have to allow yourself to place trust in your lowest level of integration to your external dependency. I realize there is a dependency to other methods in your tests, but I find that Add and Find methods are "low level" methods that are very easy to verify.
They essentially test each other as they are basically inverse methods.
Add can easily build preconditions for find
Find can verify that an add was successful.
I can't think of a scenario where a failure in either wouldn't be caught by your test
Your testAdd method has a problem: it depends on the assumption that ItemDao.list functions properly, and yet ItemDao is the Class that you're testing. Unit tests are meant to be independent, so a better approach is use plain JDBC -as #Amir said- to verify if the record was introduced in the database.
If you're using Spring, you can relay on AbstractTransactionalDataSourceSpringContextTests to access JDBCTemplate facilities and assure a rollback after the test was executed.
I use direct JDBC (using Spring's JdbcTemplate) to test the DAO methods. I mean I call the DAO methods (which are Hibernate base), and then confirm them using JDBC direct SQL calls.
The smallest unit under test for class-based unit testing is a class.
To see why, consider that you could, in theory, test each method of the class in isolation from all other methods by bypassing, stubbing or mocking them. Some tools may not support that; this is theory not practice, assume they do.
Even so, doing things that way would be a bad idea. The specification of an individual function by itself will vary between vaguely meaningless and verbosely incomprehensible. Only in the pattern of interaction between different functions will there exist a specification simpler than the code that you can profitably use to test it.
If you add an item and the number of items reported increases, things are working. If there is some way things could not be working, but nevertheless all the patterns of interaction hold, then you are missing some needed test.

How do you use Java 1.6 Annotation Processing to perform compile time weaving?

I have created an annotation, applied it to a DTO and written a Java 1.6 style annotationProcessor. I can see how to have the annotationProcessor write a new source file, which isn't what I want to do, I cannot see or find out how to have it modify the existing class (ideally just modify the byte code). The modification is actually fairly trivial, all I want the processor to do is to insert a new getter and setter where the name comes from the value of the annotation being processed.
My annotation processor looks like this;
#SupportedSourceVersion(SourceVersion.RELEASE_6)
#SupportedAnnotationTypes({ "com.kn.salog.annotation.AggregateField" })
public class SalogDTOAnnotationProcessor extends AbstractProcessor {
#Override
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
//do some stuff
}
}
You are looking for "Instrumentation", which is what frameworks like AspectJ do. In this case you have to specify a jar in the command line with the "-agent" option, and then have the possibility to filter all loaded classes. During this filter step you can check for annotations, and modify the bytecode before it gets loaded in the virtual machine. Libraries for doing the actual bytecode modification include "asm", and maybe the highlevel wrappers "cglib" and "javassist". You could even precompile your classes to generate a list of classes which have to be instrumented by you, to make filtering in the beginning a bit faster.
See java.lang.instrumentation for more info.
By design, the annotation processing facility does not allow direct modification of the source code being processed. However, one can generate subclasses of the type being processed or the superclass of the type being processed. With some planning, this does allow some of the effect of modifying the type in question. I've written up an example of how this can fit together; see this blog entry for a more detailed explanation and some sample code.
You have to use internal compiler's classes – some inspiration:
AOP or APT for overriding methods from super classes
RomanNumeralProcessor.java
Java Multiline String
But it is brinkmanship. Your program will compile only on Sun/OpenJDK and there can be problems in future versions (internal API can change). Although once compiled, it is standard bytecode and will run everywhere.
BTW: if you want use it in Eclipse, you should add some special support for it because Eclipse uses non-standard compiler. Your design should be more complex and you should add a level of abstraction to your processor – like Lombok does.
You have to extend the javac compiler for this, which means building your program won't be as portable as a regular application. See http://weblogs.java.net/blog/cayhorstmann/archive/2006/06/say_no_to_prope.html for more details on how someone achieved this.

Categories

Resources