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.