Make method accept only parameter with particular annotation - java

I have a method
public static void injectConfiguration(#Configurable Object bean) {}
And I have a class which holds field
public class LauncherComponentsHolder {
#Configurable
public RoomDao roomDao;
And I have main class, where I call that method and pass him that:
LauncherComponentsHolder root = new LauncherComponentsHolder();
root.roomDao = new RoomDaoImpl();
root.guestDao = new GuestDaoImpl();
root.maintenanceDao = new MaintenanceDaoImpl();
ConfigInjector.injectConfiguration(root.roomDao);
ConfigInjector.injectConfiguration(root.guestDao);
ConfigInjector.injectConfiguration(root.maintenanceDao);
Problem is that the method accepts all the 3 parameters, (no warnings, errors, nothing) however only roomDao is annotated. Annotation itself:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER, ElementType.FIELD})
public #interface Configurable {
}
How to make the restriction, so that injectConfiguration(#Configurable Object bean) would accept only field (or class instance) annotated with Configurable ?

You can accomplish this by using an annotation processor.
An example of such a tool is the Checker Framework.
It enables you to write type annotations in your program, then it type-checks the type annotations at compile time. It issues a warning if the type annotations in your program are not consistent with one another.
The easiest way for you to implement the checking would be to use the Subtyping Checker.
Here is an example from its manual:
import myPackage.qual.Encrypted;
...
public #Encrypted String encrypt(String text) {
// ...
}
// Only send encrypted data!
public void sendOverInternet(#Encrypted String msg) {
// ...
}
void sendText() {
// ...
#Encrypted String ciphertext = encrypt(plaintext);
sendOverInternet(ciphertext);
// ...
}
void sendPassword() {
String password = getUserPassword();
sendOverInternet(password);
}
When you invoke javac using a couple extra command-line arguments, javac issues an error for the second invocation of sendOverInternet but not the first one:
YourProgram.java:42: incompatible types.
found : #PossiblyUnencrypted java.lang.String
required: #Encrypted java.lang.String
sendOverInternet(password);
^

Related

Spring AOP - How to pass in a different number of parameters?

I have an Aspect:
#Aspect
#Component
public class BusinessAspect {
#Around("#annotation(Business)")
public Object getCorrespondingBusiness(ProceedingJoinPoint joinPoint, Business business) throws Throwable {
//BEFORE METHOD EXECUTION
Object data = joinPoint.getArgs()[0]; // gets first argument
int businessNumber = business.value(); // gets # in annotation
BusinessObj correspondingBusiness = getBusiness614(); // will make modular later
// This is where ACTUAL METHOD will get invoke
Object result = joinPoint.proceed( new Object[] { data, correspondingBusiness} );
// AFTER METHOD EXECUTION
System.out.println(result);
return result;
}
private BusinessObj getBusiness614() {
return valid business..
}
}
And here is the method that needs to access that correspondingBusiness object:
#Business(614)
public BusinessRule rangeFromGreaterThanRangeThrough(BusinessProfile businessProfile) {
return BusinessRule.businessRuleBuilder()
.withParameter("from", ...)
.withParameter("through", ...)
.withCrudOperationAction(...)
.withCrudOperationAction(...)
.setBusiness(correspondingBusiness) // not recognizing the parameter. compilation error?
).build();
}
Essentially, my issue is the correspondingBusiness object is not being recognized. I understand that you can manipulate and change parameters, but can you pass in extra parameters, or can you only change them? If I can only change them, how would I make it so I can call this method without having to pass in a second parameter? Creating an overload for each of these seems like a lot of unnecessary code.
Thank you!
Edit:
Here is the annotation interface for clarity:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Business {
int value();
}
The solution can be found in Spring documentation:
The parameter binding in advice invocations relies on matching names used in pointcut expressions to declared parameter names in (advice and pointcut) method signatures.
Define your Around advice as shown below and business parameter will be passed to the advice method.
#Around("execution(* *(..)) && #annotation(business)")

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.

aspect is not invoked

I've got a simple aspect that supposed to set the value of class fied, that has annotation #GuiceInject.
Originally I have this
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository repository;
And I expect to get similar to this
private IRacesRepository repository = GuiceInject.getInstance(IRacesRepository.class);
And here is my aspect
public aspect InjectionAspect {
Object around(): get(#GuiceInject * *) {
System.out.println(thisJoinPointStaticPart);
// instantiate object as it supposed to be null originally
return GuiceInjector.getInstance(thisJoinPoint.getTarget().getClass());
}
}
As far as I understand - I am new to AOP - it supposed to replace get invokations of the field with the code in aspect.
It compiles fine, but when I run the application - nothing happens. I get NullPointerException for readRaces method as it stays null so aspect did not work.
My main class looks like this
public class Example {
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository racesRepository;
private void execute() {
System.out.println("List of races: " + racesRepository.readRaces());
}
public static void main(String[] args) {
new Example().execute();
}
}
What is the problem? Annotation has this definition
#Target(ElementType.FIELD)
// make annotation visible in runtime for AspectJ
#Retention(RetentionPolicy.RUNTIME)
public #interface GuiceInject {
Class<? extends AbstractModule> module();
}
Please try to redefine pointcut syntax as
Object around(): get(#package.subpackage.GuiceInject * *.*)
Correct field signature must specify the type of the field, the declaring type, and name. If your annotation is in different package, it should be fully qualified.

No annotations present when reflecting a method using class.getDeclaredMethod

I'm writing some unit tests using reflection, and I'm having trouble retrieving annotations from method parameters.
I declared this interface:
private interface Provider {
void mock(#Email String email);
}
And I'm trying to reflect this method, as follows:
Class stringClass = String.class;
Method method = Provider.class.getDeclaredMethod("mock", String.class);
AnnotatedType annotatedType = method.getAnnotatedParameterTypes()[0];
Annotation annotation = annotatedType.getAnnotation(Annotation.class);
I'm expecting that annotation variable holds an instance of #Email annotation, but instead, its value is null.
Even this simple check returns false:
method.isAnnotationPresent(Email.class)
So, how can I retrieve the annotations for an specific param when reflecting a method?
Updated
It seems that in order to retrieve the parameters annotation I need to call method.getParameterAnnotations(). But the problem with this is that I don't know what annotations belong to what methods.
If you want annotation to be visible during program execution, you need to annotate it with #Retention(RetentionPolicy.RUNTIME):
private interface Provider {
void mock(#Email String email);
}
#Retention(RetentionPolicy.RUNTIME)
public #interface Email{}
#Test
public void test_annotation_existence() throws NoSuchMethodException {
Method method = Provider.class.getDeclaredMethod("mock", String.class);
Annotation[] firstParameterAnnotationsArray = method.getParameterAnnotations()[0];
boolean isAnnotationPresent = isAnnotationPresent(firstParameterAnnotationsArray, Email.class);
Assert.assertTrue("Annotation not present!", isAnnotationPresent);
}
private boolean isAnnotationPresent(Annotation[] annotationsArray, Class clazz) {
if (annotationsArray == null)
throw new IllegalArgumentException("Please pass a non-null array of Annotations.");
for(int i = 0; i < annotationsArray.length; i++ ) {
if (annotationsArray[i].annotationType().equals(clazz))
return true;
}
return false;
}
You have to make a distinction between the Java 8 type annotations and the (since Java 5) parameter annotations. The crucial thing about type annotations, is, that you have to declare the possibility of using your annotation as type annotation explicitly.
Consider the following example:
public class AnnoTest {
#Retention(RetentionPolicy.RUNTIME)
#interface Email {}
void example(#Email String arg) {}
public static void main(String[] args) throws ReflectiveOperationException {
Method method=AnnoTest.class.getDeclaredMethod("example", String.class);
System.out.println("parameter type annotations:");
AnnotatedType annotatedType = method.getAnnotatedParameterTypes()[0];
//Annotation annotation = annotatedType.getAnnotation(Annotation.class);
System.out.println(Arrays.toString(annotatedType.getAnnotations()));
System.out.println("parameter annotations:");
System.out.println(Arrays.toString(method.getParameterAnnotations()[0]));
}
}
it will print
parameter type annotations:
[]
parameter annotations:
[#AnnoTest$Email()]
In this case the annotation is a property of the parameter.
Now change it to (note the #Target)
public class AnnoTest {
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE_USE)
#interface Email {}
void example(#Email String arg) {}
public static void main(String[] args) throws ReflectiveOperationException {
Method method=AnnoTest.class.getDeclaredMethod("example", String.class);
System.out.println("parameter type annotations:");
AnnotatedType annotatedType = method.getAnnotatedParameterTypes()[0];
//Annotation annotation = annotatedType.getAnnotation(Annotation.class);
System.out.println(Arrays.toString(annotatedType.getAnnotations()));
System.out.println("parameter annotations:");
System.out.println(Arrays.toString(method.getParameterAnnotations()[0]));
}
}
which will print
parameter type annotations:
[#AnnoTest$Email()]
parameter annotations:
[]
instead. So now, the annotation is a feature of the parameter type, i.e. String. Conceptionally, the parameter type of the method is now #Email String (which seems to be the most logical choice, as it allows declaring types like List<#Email String>, but you have to understand how these new type annotations work and it doesn’t work together with pre-Java 8 libraries).
Care must be taken when enabling an annotation for both, parameters and type use, as this can create ambiguous annotations.
If that happens, the compiler will record the annotations for both, the parameter and the type, e.g.
when you change the target in the example to #Target({ElementType.TYPE_USE, ElementType.PARAMETER}), it will print
parameter type annotations:
[#AnnoTest$Email()]
parameter annotations:
[#AnnoTest$Email()]
similar issues may arise at method return types, resp. field types when enabling an annotation for “type use” and methods, resp. fields.

use of custom annotations

I found several related (not duplicate) question to this, but they didn't satisfy me.
I am unable to understand where and why to use custom annotations?
I read an example of custom annotation in a book, but it was not explained thoroughly.
#interface MyAnno
{
String str();
int val();
}
class MyClass
{
#MyAnno(str = "Annotation example", val = 100)
public static void myMeth()
{
System.out.println("Inside myMeth()");
}
}
class CustomAnno
{
public static void main(String args[])
{
MyClass.myMeth();
}
}
The output is as expected Inside myMeth().
I am having few questions regarding this example.
1- How can I use String str() and int val() in this program? OR
What is the use of any abstract method of an custom annotation?
2- Why custom annotations. I mean that what effect they are having on any code.
3- How can I create an annotation which is having effects like #override is having?(I mean any kind of effect which can be noticed)
If this example is useless for you, then please give me a suitable small example in which a custom annotation is used.
Three main reasons to use custom annotations are:
To reduce the effort of writing code (a compile-time annotation processor generates code for you). Here is a tutorial: part 1, part 2.
To provide additional correctness guarantees (a compile-time annotation processor warns you about errors). One nice tool for this is the Checker Framework, which prevents null pointer dereferences, concurrency errors, and more.
To customize behavior (at run time, your code checks for the annotation using reflection and behaves differently depending on whether the annotation is present). Frameworks such as Hibernate use annotations this way; also see an Oracle article.
In each case, use of annotations reduces the likelihood of errors in your code, compared to other non-annotation approaches.
Here is a minimal example. The following code demonstrates use of custom annotation.
It is about Employees and Benefits. If we have a requirement such that BasicBenefits has to be applied to all types of employess then we can come up with custom annotation such as BasicBenefits, and annotate all types of Employee implementations (e.g. CorporateEmployee, ContractEmployee, ManagerEmployee etc. etc.) with the BasicBenefits.
Custom Annotation Class(interface)
import java.lang.annotation.*;
#Inherited
#Documented
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface BasicBenefits {
String bId() default "B-101";
String bName() default "General Class A Employee";
}
Class using the custom annotation(no need of any imports):
#BasicBenefits(bId="B-400", bName="General Plus Class A Employee")
public class Employee {
String eId;
String eName;
public Employee(String eId, String eName){
this.eId = eId;
this.eName = eName;
}
public void getEmployeeDetails(){
System.out.println("Employee ID: "+eId);
System.out.println("Employee Name: "+eName);
}
}
Driver class to test out the above.
import java.lang.annotation.Annotation;
public class TestCustomAnnotationBasicBenefits {
public static void main(String[] args) throws Exception{
Employee emp = new Employee("E-100", "user3320018");
emp.getEmployeeDetails();
Class reflectedClass = emp.getClass();
Annotation hopeBenefitAnn = reflectedClass.getAnnotation(BasicBenefits.class);
BasicBenefits bBenefits = (BasicBenefits)hopeBenefitAnn;
System.out.println("Benefit ID: "+bBenefits.bId());
System.out.println("Benefit Name: "+bBenefits.bName());
}
}
Your code look almost there, just two things need to be included in the main method.
1.) Need reference to MyClass
2.) Need to get the annotation using reflection from MyClass.
Here is a bit modified code from what you have:
#Inherited
#Documented
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnno
{
String str();
int val();
}
//using above custom annotation on class level
//can also use method level
//just need to change t #Target(ElementType.METHOD)
#MyAnno(str = "Annotation example", val = 100)
class MyClass
{
public static void myMeth()
{
System.out.println("Inside myMeth()");
}
}
import java.lang.annotation.Annotation;
class CustomAnno
{
public static void main(String args[])
{
//1. getting reference to the class where the custom annotation is applied.
//2. then getting the annotation to get the values
MyClass myClass = new MyClass();
Class cls = myClass.getClass();
Annotation getMyAnno = cls.getAnnotation(MyAnno.class);
MyAnno myAnno = (MyAnno)getMyAnno;
MyClass.myMeth(); //left this as is.
System.out.println("myAnno.str(): "+ myAnno.str());
System.out.println("myAnno.str(): "+ myAnno.val());
}
}
The abstract methods of the annotation define the values you can pass to it (in your case str = "Annotation example", val = 100). You can access them using reflection (Method.<T>getAnnotation(Class<T>)). Custom annotations don’t have direct impact. They are only useful if you evaluate them.
Note that you have to annotate your custom annotation with #Retention(value=RUNTIME) to be able to read it via reflection.
To be of any use, annotations must be parsed first. The built-in annotations (such as #Override or #FunctionalInterface, to name the most obvious ones) are parsed by the compiler itself. As for custom annotations, these guys are commonly parsed by third-party frameworks, although we can also use the reflection mechanism to demonstrate this technique in standalone code.
By way of an example, the code below changes its behaviour at run time depending on the value of the field declared in the custom annotation named #SwitchingAnnotation:
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#interface SwitchingAnnotation{
boolean flag();
}
public class Worker{
void doThis(){ System.out.println("Doing this"); }
void doThat(){ System.out.println("Doing that"); }
#SwitchingAnnotation(
flag = false
)
public void work(boolean flag) {
if (flag) doThis();
else doThat();
}
}
class Test{
public static void main(String[] args) {
try{
SwitchingAnnotation sw = Worker.class.getMethod("work", boolean.class)
.getAnnotation(SwitchingAnnotation.class);
new Worker().work(sw.flag()); // prints Doing that
}
catch(NoSuchMethodException nsme){
System.out.println(nsme);
}
}
}

Categories

Resources