'How to use AsmVisitorWrapper to visit an array inside an annotation and merge its content with another array
(repository with an example of what I'm trying to do is here)
It's my first time using ByteBuddy and I've found myself struggling to merge the contents of two @RequestMapping
annotations.
Something like this:
@Validated
@RequestMapping(
path = "/somePrefix",
produces = {"application/text", "application/xml"},
consumes = {"application/text", "application/xml"},
params = {"exampleParam1", "exampleParam2"},
headers = {"key=val", "nokey=val"}
)
public interface ExampleInterface {
@RequestMapping(value = {"/someEndpoint"}, method = {RequestMethod.POST}, produces = {"application/json"}, consumes = {"application/json"})
ResponseEntity<String> someEndpointMethod(String value);
}
And pretending to having this:
public interface ExampleInterface {
@RequestMapping(
value = {"/somePrefix/someEndpoint"},
method = {RequestMethod.POST},
produces = {"application/text", ,application/xml","application/json"},
consumes = {"application/text", "application/xml","application/json"},
params = {"exampleParam1", "exampleParam2"},
headers = {"key=val", "nokey=val"}
)
ResponseEntity<String> someEndpointMethod(String value);
}
I've seen that editing the values can be done replacing the value when the method annotation object is visited, for example:
[...]
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (Type.getDescriptor(RequestMapping.class).equals(descriptor)) {
return new AnnotationVisitor(Opcodes.ASM9, super.visitAnnotation(descriptor, visible)) {
@Override
public AnnotationVisitor visitArray(String name) {
if ("produces".equals(name)) {
return new AnnotationVisitor(Opcodes.ASM9, super.visitArray(name)) {
@Override
public void visit(String name, Object value) {
// I'd like to receive an array as value, so I can provide one with all values merged
boolean tryToMerge = false;
if (tryToMerge) {
//I cannot return array with everything
Object[] newValue = new Object[]{value};
value = Arrays.copyOf(newValue, newValue.length + originalAnnotation.produces().length);
System.arraycopy(originalAnnotation.produces(), 0, value, newValue.length, originalAnnotation.produces().length);
} else {
//I can only replace a single value
value = originalAnnotation.produces()[0];
}
// How to set an array in produces?
super.visit(name, value);
}
};
} else {
return super.visitArray(name);
}
}
};
} else {
return super.visitAnnotation(descriptor, visible);
}
}
[...]
However, I'm receiving the array values one per one via visit()
and I cannot just return an array with the two values I want to merge (["application/text", "application/xml"]
) because it's expecting an String
object. I can substitute the value that I'm receiving but I cannot add more.
Besides that, the headers
and params
arrays are not being visited, which seems logical because no values are on those arrays. However, I'm not sure how I should visit those fields in the @RequestMapping
on the method so I can insert the values picked from the class one.
What I am missing here?
Thanks in advance.
Solution 1:[1]
In such a case, you can override the onEnd()
method of AnnotationVisitor
. When invoked, you know that all values are processed and you can invoke super.onValue()
from there before delegating to super.onEnd()
. If you know the replacements beforehand, you can also simply leave the onValue
method empty to drop all existing values and then repopulate the array in the end.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Rafael Winterhalter |