I am trying to develop a tool that needs to work with annotations.
One important feature is to target an element annotated with an annotation and change its value, i.e.
// from this
#Annotation(value = "foo")
class SomeClass {}
// to this
#Annotation(value = "bar")
class SomeClass {}
I made an attempt in which first I remove the annotation with an AsmVisitorWrapper and then I re-add the annotation with the modified value.
Sadly this does not seem to work.
I used the byte-buddy-maven-plugin to add this transformation. The error happens during the transform goal. I tracked down the generic error to a NullReferenceException: the Asm ClassVisitor seems to take place after the annotateType() step and tries to apply some visit step to the new attached annotation value. I think the NullReferenceException is caused by the visitor beacuse to remove an annotation you need to return null.
I made a test repository on github where I pushed my attempt. Hoping it helps to understand what I need to achieve. https://github.com/Fed03/bytebuddy-switch-annotation-test
Thanks
This is indeed a bug in Byte Buddy that is now fixed on master and will be part of version 1.10.2. The problem is that you are removing an annotation that you are adding and this was not considered as a scenario.
However, even with this fix, your problem is not solved despite a green build. You would need to discriminate better to tell Byte Buddy which annotation you are removing. I would recommend you to transform an annotation rather then removing it to later add it again. Any matcher that discriminates to which of the two annotations needs to be removed needs to partially implement such change discovery already which is why it should not be much more difficult to implement the transformer in the first place.
Related
I have a configuration property that needs to be sent to another system as part of a post body.
lets say for example if have an application.properties file with
my.property.raw=${parameter:some-identifier}
I also have a #ConfigurationProperties annotated class that might look like this
#ConfigurationProperties(prefix = "my.property")
public class Properties {
private String raw;
// getters and such
}
The problem i have is that Spring will see ${parameter:some-identifier} and immediately assumes i want it to be injected with a property names "parameter" and if its not there, put "some-identifier". i have tried to set the property to ${parameter:${parameter:some-identifier}} but Spring seems to have accounted for recursive property injection and it still comes out as "some-identifier".
I know i can write some code around that and replace the ":" with something else and change it back after the fact but i'm trying to make this work without any code change.
So in summation, Spring boot sees this as a configuration property ${parameter:some-identifier} upon injection into the ConfigurationProperties annotated class, the value ends up being some-identifier. What i want to happen is the value in the Properties class after Spring does its variable replacement process is ${parameter:some-identifier} as this injectable format of a value is intended for a down stream system that is expecting that format. Preferably, without changing any code. Maybe there is some special formatting i can use to tell spring to ignore this specific property.
Thanks for your time! and yes i realize this is probably the opposite of what people normally want from Spring.
As stated by #Kayaman this is indeed a duplicate of
Escape property reference in Spring property file
The solution i pulled from there, with some modification, was this:
${dollar:$}{parameter:some-identifier}
https://stackoverflow.com/a/48897422/4621716
I did this because i also don't have control over the process that is generating that application.properties beyond changing existing values.
I hope this helps and i wish i could give #Kayaman credit for pointing me in the right direction but i guess i'll accept my own answer. Thanks.
Background
We are currently implementing an application using hexagonal architecture. Our REST API DTOs are mapped to our entities via MapStruct. This works fine. (Though, it would be much nicer if MapStruct would have support for hierarchical structures.)
Problem
However, we are facing a problem which is best described by the following example:
Consider you have an entity Person that stores the date of birth. Now, this
entity has a method which might be called int calculateAge().
The REST API's PersonDto will get an attribute int age.
Now, we want MapStruct to generate this mapping for us. Our approach was to try to configure #Mapping(target = "age", ...) to use the int calculateAge() method as source, but we did not succeed.
Believing this might be a straightforward application of MapStruct, we were quite disappointed to not come up with a clean solution after searching on this topic for hours.
Solutions
We found two solution approaches that work, but are (in our opinion) not really maintainable:
Use #Mapping(expression = "java(...)")
Use #AfterMapping to post process the constructed DTO and implement the required mappings in the annotated method
Question
Is there a cleaner way to achieve our goal, something which might look like this #Mapping(sourceMethod = "calculateAge", target = "age)?
Is there a cleaner way to achieve our goal, something which might look like this...
No, there isn't as of the MapStruct latest stable version (1.4.1.Final) of time of writing this answer. You have basically two choices which heavily depends what exactly and how you want to map the fields. I describe shortly in what case each solution is suitable for:
The first solution using expression introduces the problem the methods are hardcoded in the annotation. I prefer this solution only in the case of simple format conversions or calculations without calling a custom method (only from the existing Java API). Anyway, even with your proposed solution it would be still hardcoded. The syntax is the only thing that changes. There is effectively no difference in terms of maintainability:
#Mapping(target = "age", expression = "java(...)") // current API
#Mapping(sourceMethod = "calculateAge", target = "age") // hypothetical
Feel free to request for such feature. This solution in any case also requires imports within the mapper (#Mapper(imports = Another.class)) as well as the "hypothetical" one.
The annotation #AfterMapping is useful in case of more complex transformations and calculations. It's not as clean as a single annotation call and in fact you still write the mapping manually, however, it brings more control over the called methods which the IDE highlights before the compilation (at least you don't need an additional IDE-specific plugin). I'd go for this solution in case I need to call my custom methods and logics.
From what I've seen, Mapstruct relies on standard getters and setters. If you want to use a specific method then Mapstruct does work with #qualifiers, but I don't think the method can be in the entity. From my experience the best solution is to use #AfterMapping, as you mentioned.
I have a VariableElement field that is annotated with a generated Annotation (which is why I can't use field.getAnnotation(annotationClass)). I need to get all parameters passed to this annotation.
Note that by "a generated Annotation" I mean that literally the Annotation class itself (not the annotated one) has been generated by an Annotation Processor. The field/class that is being annotated is in the handwritten source code.
It didn't look like it'd be that hard, so far I've come up with this:
for (AnnotationMirror annotation : field.getAnnotationMirrors()) {
Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValueMap = annotation.getElementValues();
messager.printMessage(Diagnostic.Kind.WARNING, annotation.toString() + ":" + annotationValueMap.toString());
}
I thought this would do it, but the output for the field is the following:
#MyAnnotation:{}
So, the processor does recognize that the field is annotated, but I'm unable to access the passed parameters. Even though the field is definetely annotated and does pass parameters with the annotation (it has to, since the annotation defines required parameters and no defaults):
#MyAnnotation(max = 387, min = 66876, ...)
private Integer myField;
Here's the generated annotation code:
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
int max();
boolean allowAuto();
int min();
}
I've clean-compiled the project multiple times, the processor never sees the values. What am I overlooking here? The processor can obviously see the annotation itself, yet the parameters passed to it are hidden.
Recall that annotation processors run as part of the compiler, in steps called "rounds". This process runs iteratively until there is no new code to compile, and then processors get one last chance to run (not necessary for this answer, but helpful for more context). Each round only the newly created types are directly given to the processor to examine.
What seems to be happening here is that during a round you are emitting a new annotation type, which should allow the processor to observe certain features about some code submitted to be compiled. However, any types created during a given round are not yet compiled until the next round begins.
For this question, we run into a conflict here - some Java sources are compiled which use an annotation that doesn't exist yet. The processor first creates the annotation, and then tries to read the newly-created annotation out of those partly-compiled sources. Unfortunately, until the annotation has been compiled, we can't actually read the annotation. Instead, we need to wait until the subsequent round (once the annotation itself has compiled), then go back to that class which has finished being compiled and examine it.
This can be implemented yourself without too much trouble, but the easiest way is often to rely on the google/auto project (specifically the auto-common library, see https://github.com/google/auto/tree/master/common), and extend their BasicAnnotationProcessor class. One of the nice features it supports is to automatically examine types and check if there are any compilation issues - if so, they are deferred until a later round so you can handle them without any type resolution issues.
Use getAnnotation(MyAnnotation.class) available from VariableElement
in your example code you can do this to get the min and max parameters
MyAnnotation myAnnotation= field.getAnnotation(MyAnnotation.class);
int max = myAnnotation.max();
int min = myAnnotation.min();
this will work unless the annotation members returns class/class[] value, in which you will get an exception if you try to get the value using this method.
more about how to get class literal values can be found on this answer
How to read a Class[] values from a nested annotation in an annotation processor
Or using annotation mirrors
for (AnnotationMirror annotation : field.getAnnotationMirrors()) {
Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValueMap = annotation.getElementValues();
annotationValueMap.forEach((element, annotationValue) -> {
messager.printMessage(Diagnostic.Kind.WARNING, element.getSimpleName().toString() + ":" + annotationValue.getValue());
});
}
In case you have more than one annotation on the field then you can iterate over the annotation mirrors and use the check types.isSameType(annotationMirror.getAnnotationType(), elements.getTypeElement(MyAnnotation.class.getName()).asType()) to find the annotation you are interested in
Yes, you will not be able to instantiate a Class object for a type which is not available in your annotation processor's classloader, and may not even have been compiled into a class file yet at all. A similar problem exists for retrieving enum constants.
There are a few wrinkles to dealing with this sort of thing:
Any annotation value that is declared as an array might come to you at compile time either as a single value, or a list of values - so any code needs a path to handle both the list and non-list case - like this
What you get may be a generic type, and if you are generating Java code or similar that wants to insert a reference to Foo.class you need to get the erasure of that type, so you don't generate Foo<Bar>.class into your generated sources.
One of the places your annotation processor is going to get run is in an IDE, on broken code still being typed, so it is important to fail gracefully in the case that code elements that, you would think, can't possibly be missing or broken or unlikely values, are. In an IDE, your annotation processor may also be kept alive for a long time and reused, so it's important not to pile up objects modeling stuff that has already been generated and emitted.
FWIW, I wrote a library to solve this and related problems, which can be found on Maven central at the coordinates com.mastfrog:annotations-tools:2.8.3.4 (check for newer versions). The usage pattern is simple:
Instantiate an instance of AnnotationUtils in an override of the init() method of your annotation processor and store it in a field
Use it to, for example, resolve a Class<?>[] into a list of string class names that you can work with inside javac, and similar
It makes it pretty straightforward to write annotation processors that do not directly depending on the classes they processes at all - which means the annotation processors (and their dependency graphs!) be completely independent of what they process, and can depend on whatever libraries they like without forcing those dependencies into the dependency graph of any project that uses them - the most common pattern is someone writes some annotations and then puts the annotation processor in the same project, or even package, and so anything the annotation processor uses becomes a dependency of every consumer of the annotations, even though those dependencies will probably never be used at runtime at all. That, it seems to me, is an antipattern worth avoiding.
I'm currently using RestEasy(2.3.6) with Jackson(1.9.9) and needing to prefix my JSON arrays with '{} &&' in order to prevent JSON hijacking.
I'm new to Jackson and am having a really hard time understanding where to insert anything like this. I'm not even sure where to insert something like this to make it happen all the time, and I would like to take it one step further and be able to specify to only prefix return values that contain JSON arrays and not regular objects.
I imagine there is a class somewhere I need to subclass and override a method, and then register that class somehow. Has anyone ever done anything like this?
Jukka, the question you linked to led me to a solution. I extended JacksonJsonProvider, and overrode the writeTo() method. There are a few conditions in there and I was able to add jg.writeRaw("{}&&"); before each place it writes the value. Also, since I'm using Spring, I had to annotate my class with #Component in order for it to be found.
Also another gotcha with creating your own JsonProvider subclass is your rest methods must have #Produces('application/json') (you should always be explicit with these anyway) or else the default JsonProvider will be used.
I'm using Oval to do validations, so we have code that looks like this:
#NotNull(errorCode = "NumberInvalid")
#NotNegative(errorCode = "NumberInvalid")
#Range(errorCode = "NumberInvalid", min=1, max=10)
protected Integer dollarAmount;
I want to be able to update the range dynamically if needed, say if we have a config file that changes every month or so, I want to be able to reflect those changes in the validation.
Is there a way I can do this with reflection so that I don't have to subclass every annotation in Oval?
As far as I'm aware this is not possible. Assuming your annotation retention is set to RUNTIME (which it would have to be for the validation to work) then what you will effectively have is a proxy class that implements the annotation interface, you won't be able to amend the values through reflection.
Though annotations looks cleaner with static codes :) but ...there is a way.. Did you tried using reflection ? can you post the approach . You can have a look at this
The purpose of reflection is to access class members (including setting fields), but it does not cover adding new members or modifying existing declarations. What you want is more similar to bytecode editing or code refactoring.