Java annotation to set field to a static instance? - java

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.

Related

Setting property value with custom runtime annotation

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.

Add property to arbitrary java bean

I want to write a function that, given an arbitrary java bean as an argument, returns an object that is a copy of that bean but that belongs to an anonymous subclass of the bean's type that contains an additional property. Let me illustrate with an example of what I have so far:
Foo.java:
import lombok.Data;
import lombol.AllArgsConstructor;
#Data
#AllArgsConstructor
public class Foo {
private String bar;
private String baz;
}
Garply.java:
public class Garply {
Foo fooWithQux(Foo foo, String quxVal) {
return new Foo(foo.bar, foo.baz) {
private String qux;
public String getQux() {
return quxVal;
}
};
}
}
This seems silly because I can never actually call getQux(), but a tool I work with uses reflection to successfully find the qux property and do what I want with it.
My issue is that I don't want to have separate fooWithQux() functions for each type that I want to be able to add the qux property to. Ideally I'd have something like beanWithQux() that accepts objects of arbitrary type. I think I could make this work with something like the following:
public T beanWithQux<T>(T bean, String quxVal) {
class BeanWithQux extends T {
private String qux;
BeanWithQux(T bean, String quxVal) {
// Here's where I'd like to copy all of the properties
// from the Bean into the BeanWithQux
qux = quxVal;
}
public getQux() {
return qux;
}
}
return BeanWithQux(bean, quxVal);
}
Here's where I'm stuck. I don't know to copy all of the properties from the given object into my new object. Anyone have ideas? Ideally there would be something I could do using lombok (I control the Foo class and can add annotations like #Builder if need be) as opposed to writing a bunch of reflection magic myself.
Thanks!
I think in this case using runtime bytecode weaving is a better approach, since you don't need to call the methods in your own codebase.

How does Java implements the methods declared in the Annotations (Metadata)?

An image taken from a book which I am going through,
The caption says it all. Please suggest or give me something as to what happens behind the scenes.
For example, how does #NotNull in Hibernate Bean Validation API works?
I know that through Reflection API, we can do something like this,
class Meta {
// Annotate a method.
#MyAnno(str = "Annotation Example", val = 100)
public static void myMeth() {
Meta ob = new Meta();
// Obtain the annotation for this method
// and display the values of the members.
try {
// First, get a Class object that represents
// this class.
Class c = ob.getClass();
// Now, get a Method object that represents
// this method.
Method m = c.getMethod("myMeth");
// Next, get the annotation for this class.
MyAnno anno = m.getAnnotation(MyAnno.class);
// Finally, display the values.
System.out.println(anno.str() + " " + anno.val());
} catch (NoSuchMethodException exc) {
System.out.println("Method Not Found.");
}
}
public static void main(String args[]) {
myMeth();
}
}
Annotations don't have any implemented code and actually don't do anything themself.
To make them "work", there should be some kind of annotation processor (initializer, loader or any class that works with annotated objects). This annotation processor checks annotation objects annotations and changes the way it is handled.
For example Spring annotation processor, when initializing an object, looks for #Autowired fields, to fill autowired fields.
Same goes for Hibernates #NotNull. it doesn't do anything actually. However, Hibernate, when persisting your object, checks if there should be something there.

Jackson: final field written to after it is set by #JsonCreator?

It seems Jackson uses reflection to write additional attributes directly into fields even if a #JsonCreator constructor was used and the field is marked as final.
Example:
public class Test {
static class X {
final String s;
#JsonCreator
public X(#JsonProperty("a") String a) {
s = "s";
}
public String getS() {
return s;
}
}
#org.junit.Test
public void ds() throws Exception {
ObjectMapper om = new ObjectMapper();
X x = om.readValue("{`a`:``, `s`: `t`}".replace('`', '"'), X.class);
assertEquals("s", x.s);
}
}
The assert will fail with org.junit.ComparisonFailure: expected:<[s]> but was:<[t]>.
Is this behavior documented anywhere? Is there anyway to disable this globally?
Also, I think this is a very dangerous design: if there are some value that should be read-only to the client, this effectively allows the client to set them even if the class is well designed according to normal immutable class guidelines.
First: yes, Jackson allows deserialization of all visible and not just those for which #JsonCreator property exists. So it is possible to set a smaller set of properties via constructor, and others via setters or fields. This may be needed for some cases like cyclic types.
As to how to prevent use of s for deserialization here. An obvious way is to add #JsonIgnore for field, although if so you will also need #JsonProperty for getS() to avoid both being removed.
But there are other settings as well, in MapperFeature.
ALLOW_FINAL_FIELDS_AS_MUTATORS: if you disable this, final fields are never used directly for deserialization
INFER_PROPERTY_MUTATORS: if you disable this, then neither fields nor setters are "pulled in" in cases where they are not otherwise visible (for fields, public is needed to be visible; for setters, even private is fine)
So may want to disable one or both settings.

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