Setting property value with custom runtime annotation - java

I am trying to come up with a custom annotation, wanted to see if my use-case fit a allowed way of using custom annotation.
I want to replicate what Spring #Value does, but instead of reading a property off of a property, i want to my custom thing.
#Documented
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public #interface EncryptedValue {
String value();
}
public Class TestEncrypted {
#EncryptedValue("dGVzdCBzdHJpbmc=");
public String someEncryptedValue;
}
I am hoping in annotation processor, i decrypt value and set to the field someEncryptedValue.
/**
*
*/
#SupportedAnnotationTypes("annotation.EncryptedValue")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CustomProcessor extends AbstractProcessor{
private Types typeUtils;
private Elements elementUtils;
private Filer filer;
private Messager messager;
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
typeUtils = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
for(Element ele : annotatedElements) {
EncryptedValue encryptedValue = ele.getAnnotation(EncryptedValue.class);
if(!ele.getKind().isField()){
messager.printMessage(Diagnostic.Kind.ERROR,"EncryptedValue is supported for field");
return false;
}
String annotationValue = encryptedValue.value();
// now get the enclosing type
Set<Modifier> modifiers = ele.getModifiers();
String nameOfVariable = ele.getSimpleName().toString();
// check to see what fields we can modify (i think we can't modify static).
messager.printMessage(Diagnostic.Kind.NOTE,"ClassType: "+ele.getSimpleName().toString()+", nameOf="+annotationValue);
String simpleName = ele.getEnclosingElement().getSimpleName().toString();
for (Element elem : roundEnv.getRootElements()) {
messager.printMessage(Diagnostic.Kind.NOTE, "Enclosing ClassName: "+elem.getSimpleName().toString());
if (elem.getSimpleName().toString().equals(simpleName)) {
for (Element variableDeclaration : elem.getEnclosedElements()) {
if (variableDeclaration instanceof VariableElement) {
messager.printMessage(Diagnostic.Kind.NOTE, "variable: "+((VariableElement) variableDeclaration).getSimpleName().toString());
}
}
}
}
}
}
return true;
}
}
I get the variable, its return types and everything, but not sure how to set value of the variable from this annotation, even if i figure it out, is it good way of using custom annotations.
*Note: This might be sample, what I am planning to do is much more complicated than above sample.

There's no way to modify existing source files via the current publicly-available API. Tools like Lombok which do this are using undocumented internal Javac features to edit the abstract syntax tree. For example, you could use the Sun compiler tree API to obtain a VariableTree, cast it to a JCVariableDecl, then modify it and hope there are no unforeseen consequences. There's no guarantee that tools like Lombok will actually work, and they could break tomorrow with no warning.
What you could do instead is have the annotated classes reference a class which your annotation processor generates, as in the following example:
public class TestEncrypted {
#EncryptedValue("dGVzdCBzdHJpbmc=");
public String someEncryptedValue =
TestEncryptedDecryptedValues.someEncryptedValue;
}
// then generate this class with the annotation processor
final class TestEncryptedDecryptedValues {
static final String someEncryptedValue = "test string";
}
Another way to do something like this would be to use the annotation processor to generate a factory object or method which creates instances of e.g. TestEncrypted with the field assigned to the decrypted value.
A good tutorial for code generation with annotation processors is here: https://deors.wordpress.com/2011/10/08/annotation-processors/
Also, as a side note in case you don't know this, String literals and names appear in the compiled class file, so none of these examples which decrypt the data at compile-time provide any security.

Related

Enforce method signature using annotations

I have written a custom annotation that I use to find methods that can be invoked via a IoT platform. It's a method level annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface DirectMethod {
String value();
}
I look this annotation up in runtime, and to have the call succeed, the expected signature must be:
#DeviceMethod("metod")
public ReturnType methodName(final String data) {...}
i.e, the return type and the input parameters are crucial.
Is there any way to have an annotation be "smart" when its target type is METHOD? Like integrated IDE warnings and such. Or do I simply have to process each annotation manually at startup and have the startup procedure fail if any method breaks my intended method contract?
Yes, you can write annotation processor to validate your calls, the only downside of this is that annotation processors needs to be passed to javac (gradle and maven support easy syntax to register them) so someone could just not do it and not see any warnings/errors.
But otherwise all you need to do is create special annotation and processor, like that:
#SupportedAnnotationTypes("com.gotofinal.direct.DirectMethod")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DirectAnnProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
TypeElement stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String");
TypeElement expectedReturnType = processingEnv.getElementUtils().getTypeElement("com.gotofinal.direct.ReturnType");
for (Element element : roundEnv.getElementsAnnotatedWith(DirectMethod.class)) {
if (! (element instanceof ExecutableElement)) {
processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation should be on method.");
continue;
}
ExecutableElement executableElement = (ExecutableElement) element;
if (! executableElement.getReturnType().equals(expectedReturnType)) {
processingEnv.getMessager().printMessage(Kind.ERROR, "Method should return ReturnType");
}
List<? extends VariableElement> parameters = executableElement.getParameters();
if (parameters.size() != 1 && parameters.get(0).asType().equals(stringType)) {
processingEnv.getMessager().printMessage(Kind.ERROR, "Method should have single String argument");
}
}
return true; // no further processing of this annotation type
}
}
And register it in META-INF/services/javax.annotation.processing.Processor file:
com.gotofinal.direct.DirectAnnProcessor
And then you can add such lib to maven/gradle as annotation processor and it should report any issues. In gradle such library must be added using annotationProcessor "my:lib:0.1" declaration.

Get enclosing class name of a VariableElement in javax.annotation.processing.Processor

I am just starting to learn annotation processing in Java. I have #MyAnnotation which targets ElementType.FIELD, In my Processor I am limiting the annotation to only allow non-null unique value. Which is working as it should.
While logging errors in case where there is infact some duplicate value set to MyAnnotation.value, I want to provide the complete path of both the existing annotations in the source code and the new duplicate one.
My Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(value = ElementType.FIELD)
public #interface ProviderColumn {
String content_id();
}
Example Parent Class.
public class EnclosingParent {
#ProviderColumn(content_id="Hello")
private String value1;
#ProviderColumn(content_id="Hello")
private String value2;
}
My AnnotationProcessor
#Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)
{
UniqueColumnNameChecker columnNameChecker = new UniqueColumnNameChecker();
try {
for (Element annotatedElement : roundEnvironment.getElementsAnnotatedWith(ProviderColumn.class)) {
// We can cast it, because we know that it is of ElementType.FIELD
VariableElement variableElement = (VariableElement) annotatedElement;
try {
ProviderColumnId providerColumnId =
new ProviderColumnId(variableElement);
// How can I get the EnclosingParent.class or even just a complete canonical class name?
// throws a DuplicateAnnotationIdException when it detects duplicate entries.
columnNameChecker.addAnnotationColumnName(providerColumnId);
}
catch (IllegalArgumentException e) {
error(variableElement, e.getMessage());
return true;
}
}
}
catch (DuplicateAnnotationIdException ex) {
error(ex.getMessage());
return true;
}
return true;
}
However I am having trouble figuring out how to obtain enclosing class information from a VariableElement. Since I'm only starting on AnnotationProcessing I'm not even sure if this is possible and I haven't been able to find any questions related to this issue on StackOverflow or anywhere else.
Expected error output
Duplicate content_id()='Hello' detected, Found at 'com.example.EnclosingParent.value1' and 'com.example.EnclosingParent.value2'.
Note: I realize I can get parent info if I define a new ElementType.TYPE Annotation and set to the Enclosing class, but I'm looking to avoid that as it adds additional responsibilities for the third party developer.
Stupid question apparently. I was looking in all the wrong places for the enclosing element, found this excellent Annotation Processor Tutorial which states:
Elements and TypeMirrors
...
You can think of it like a XML file you try to parse (or an abstract syntax tree in compiler construction)
And I realized the Element itself might contain all the information I need.
Just need to call variableElement.getEnclosingElement() to get the parent Element
From there on it was easy, you can get enclosing class name like so:
element.getEnclosingElement().asType().toString();
This however doesn't guarantee the enclosing element to be of TypeElement. So just use instanceof to ensure that it is.

Java annotation to set field to a static instance?

I've been playing with annotations, and I'm wondering how to go about doing this. What I'd like to do is to be able to have a field declared in a class and annotated such that the field will be initialized with a static instance of the class.
Given an annotation like this:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME) //or would this be RetentionPolicy.CLASS?
public #interface SetThisField {
}
Something like this:
public class Foo {
#SetThisField
private Bar bar;
}
I've played around with using a parser and setting this at runtime, which works but isn't as elegant as I'd like.
I can't find any really good examples of RetentionPolicy.CLASS but the documentation seems to indicate that I could somehow make the declaration of "bar" get compiled into this:
private Bar bar = Bar.getInstance();
It wouldn't look that way in the source code of course, but it would in the byte code and it would behave like that at runtime.
So am I off base here? Is this possible? Or is the parser the way to go with it?
UPDATE: This is the guts of the parser I'm using
public static void parse(Object instance) throws Exception {
Field[] fields = instance.getClass().getDeclaredFields();
for (Field field : fields) {
//"Property" annotated fields get set to an application.properties value
//using the value of the annotation as the key into the properties
if (field.isAnnotationPresent(Property.class)) {
Property property = field.getAnnotation(Property.class);
String value = property.value();
if (!"".equals(value)) {
setFieldValue(instance, field, properties.getProperty(value));
}
}
//"Resource" annotated fields get static instances of the class allocated
//based upon the type of the field.
if (field.isAnnotationPresent(Resource.class)) {
String name = field.getType().getName();
setFieldValue(instance, field, MyApplication.getResources().get(name));
}
}
}
private static void setFieldValue(Object instance, Field field, Object value) throws IllegalAccessException {
boolean accessibleState = field.isAccessible();
field.setAccessible(true);
field.set(instance, value);
field.setAccessible(accessibleState);
}
I would suggest doing the replacement at run time. This is much simpler to implement and test. Changing the byte code at build time is relatively error prone and tricky to get right. For example you would need to understand how byte code is structured and in this case how to add the code to all the constructors in the right place in the code.
If you make the retention RUNTIME, you can have a library which examines the annotation and sets the value after the object is created.

Is this a good pattern for annotation processing?

I've got a fairly standard Spring webapp, and I have a number of custom annotations that I would like to use to denote the requirements and constraints applied to a given web-service method. For instance, I might apply an #RequiresLogin annotation to any method that requires a valid user session, and #RequiresParameters(paramNames = {"name", "email"}) on a method that requires that "name" and "email" be set, and so on.
In support of this I implemented an ad-hoc utility for validating a method's annotated constraints at runtime, which basically followed a pattern of:
Map<Class<? extends Annotation>, Annotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
if (annotations.containsKey(AnnotationType1.class)) {
AnnotationType1 annotation = (AnnotationType1)annotations.get(AnnotationType1.class);
//do validation appropriate to 'AnnotationType1'
}
if (annotations.containsKey(AnnotationType2.class)) {
AnnotationType2 annotation = (AnnotationType2)annotations.get(AnnotationType2.class);
//do validation appropriate to 'AnnotationType2'
}
//...
This works fine, but has become a bit unwieldy as I have added additional annotations. I'd like to replace it with something a bit more maintainable. Ideally I'd like to be able to do:
List<ValidatableAnnotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
for (ValidatableAnnotation annotation : annotations) {
annotation.validate(request);
}
But I'm pretty sure that is not possible since annotations themselves cannot contain executable code and since the compiler will not let me extend java.lang.annotation.Annotation (not that I'd know how to go about allowing executable code to be contained in an annotation even if the compiler let me try).
What annotations can contain, however, is a nested inner class, and that inner class can do anything that a normal Java class can do. So what I've come up with based upon that and in the interest of keeping my validation code as closely associated with the annotation being validated as possible is:
public interface AnnotationProcessor {
public boolean processRequest(Annotation theAnnotation, HttpServletRequest request);
}
And then the annotations can be implemented like:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface RequiresLogin {
public static class Processor implements AnnotationProcessor {
#Override
public boolean processRequest(Annotation theAnnotation, HttpServletRequest request) {
if (! (theAnnotation instanceof RequiresLogin)) {
//someone made an invalid call, just return true
return true;
}
return request.getSession().getAttribute(Constants.SESSION_USER_KEY) != null;
}
}
}
Which keeps the validation logic nice and tightly coupled with the annotation that is being validated. Then all my ad-hoc validation code can be replaced with:
List<Annotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
for (Annotation annotation : annotations) {
processAnnotation(annotation, request);
}
private static boolean processAnnotation(Annotation annotation, HttpServletRequest request) {
AnnotationProcessor processor = null;
for (Class<?> processorClass : annotation.annotationType().getDeclaredClasses()) {
if (AnnotationProcessor.class.isAssignableFrom(processorClass)) {
try {
processor = (AnnotationProcessor)processorClass.newInstance();
break;
}
catch (Exception ignored) {
//couldn't create it, but maybe there is another inner
//class that also implements the required interface that
//we can construct, so keep going
}
}
}
if (processor != null) {
return processor.processRequest(annotation, request);
}
//couldn't get a a processor and thus can't process the
//annotation, perhaps this annotation does not support
//validation, return true
return true;
}
Which leaves no more ad-hoc code that needs to be revised every time I add a new annotation type. I just implement the validator as part of the annotation, and I'm done.
Does this seem like a reasonable pattern to use? If not then what might work better?
You may want to investigate AOP. You can advise methods that expose certain annotations and perform pre/post processing accordingly.
I would just like to add that while AOP would be a good solution, the Spring framework already provides this functionality by way of the #Secured annotation.
#Secured("ROLE_USER")
public void foo() {
}
Spring also supports JSR-303 validation with the #Valid annotation. So for these use cases at least, it seems you are re-inventing the wheel.
IMHO one could think about the Visitor pattern in combination with a factory. The factory will return a wrapper object that knows the exact annotation type and which the visitor will be able...
class MyVisitor {
public void visit(VisitableAnnotationType1 at) {
//something AnnotationType1 specific
}
public void visit(VisitableAnnotationType2 at) {
//something AnnotationType2 specific
}
... // put methods for further annotation types here
}
class VisitableFactory {
public abstract class VisitableAnnotation {
public abstract void accept(MyVisitor visitor);
}
class VisitableAnnotationType1 implements VisitableAnnotation {
public void accept(MyVisitor visitor) {
visitor.visit(this);
}
}
public static VisitableAnnotation getVisitable(Annotation a) {
if(AnnotationType1.class.isAssignableFrom(a.getClass()) {
//explicitely cast to the respective AnnotationType
return new VisitableAnnotationType1((AnnotationType1)a);
} else if (AnnotationType2.class.isAssignableFrom(a.getClass()) {
//explicitely cast to the respective AnnotationType
return new VisitableAnnotationType1((AnnotationType1)a);
}
}
}
As we cannot extend Annotation, we need those wrapper classes in the factory. You could also pass the original annotation which is then contained in that wrapper class.
What you have to do: For each new AnnotationType add a new "wrapper" class to the factory, extend the factory's
getVisitable()
method accordingly and also add an according method to the Visitor:
public void doSomething(VisitableAnnotationTypeXYZ at) {
//something AnnotationTypeXYZ specific
}
now the generic validation (or whatever) code looks like:
List<ValidatableAnnotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
MyVisitor visitor = new MyVisitor();
for (ValidatableAnnotation annotation : annotations) {
VisitableFactory.getVisitable(annotation).accept(visitor);
}
The visiting works by the indirection that the visited object calls the visitor with itself as the argument and thus the correct visit method will be invoked.
Hope that helps ;-)
Code is not tested, though...

How do app servers inject into private fields?

I saw this question
Inject into private, package or public field or provide a setter?
about how to manually inject into annotated private fields (The way is adding setters
or through a constructor)
But, the point is how do an application server (like glassfish, axis2, jboss, ...)
is able to inject into a final private field (without adding setters or constructors
to the user class)?
Quoting the cited question:
public SomeClass {
#Inject
private SomeResource resource;
}
Do they use a customized JVM (not the standard one) that allows to access private fields?
Thanks
It's a simple reflection "trick". It relies on the Field.setAccessible() method to force the member to be accessible programmatically:
Set the accessible flag for this
object to the indicated boolean value.
A value of true indicates that the
reflected object should suppress Java
language access checking when it is
used. A value of false indicates that
the reflected object should enforce
Java language access checks.
The Reflection API is used to get a handle on the field, setAccessible() is called, and then it can be set by the injection framework.
See an example here.
No magic, no custom VM.
With the help of skaffman I coded this simple example on how to inject without setters.
Perhaps it helps (It did to me)
//......................................................
import java.lang.annotation.*;
import java.lang.reflect.*;
//......................................................
#Target(value = {ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#interface Inject {
}
//......................................................
class MyClass {
#Inject
private int theValue = 0;
public int getTheValue() {
return theValue;
}
} // class
//......................................................
public class Example {
//......................................................
private static void doTheInjection(MyClass u, int value) throws IllegalAccessException {
Field[] camps = u.getClass().getDeclaredFields();
System.out.println("------- fields : --------");
for (Field f : camps) {
System.out.println(" -> " + f.toString());
Annotation an = f.getAnnotation(Inject.class);
if (an != null) {
System.out.println(" found annotation: " + an.toString());
System.out.println(" injecting !");
f.setAccessible(true);
f.set(u, value);
f.setAccessible(false);
}
}
} // ()
//......................................................
public static void main(String[] args) throws Exception {
MyClass u = new MyClass();
doTheInjection(u, 23);
System.out.println(u.getTheValue());
} // main ()
} // class
Run output:
------- fields : --------
-> private int MyClass.theValue
found annotation: #Inject()
injecting !
23
It's also worth noting, that some frameworks utilize bytecode engineering via a custom classloader to achieve the same result without the cost of Reflection (reflection can be pretty expensive at times)

Categories

Resources