0

I have a custom Annotation like this -

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ControllerAction {
    String value();
}

I have a class that uses this annotation like this -

public class TestController extends AbstractController {

    public TestController () {
        super();
    }

    @ControllerAction("add")
    public void addCandidate(){

    }

}

The super class looks like this -

public abstract class AbstractController {

    public AbstractController (){

    }

    public CustomBean processRequest(ServletAction action, HttpServletRequest request) {
        Class<AbstractController > controllerClass = AbstractController.class;
        for (Method method : controllerClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(ControllerAction.class)) {
                Annotation annotation = (ControllerAction) method.getAnnotation(ControllerAction.class);
                if(annotation != null){
                    if(annotation.value().equals(action.getAction())){
                        method.invoke(controllerClass.newInstance());
                    }
                }
            }
        }
        return null;
    }
}

The processRequest(...) method in AbstractController is called from a servlet directly. The processRequest() method figures out the servlet action, and based on that, it should call the method appropriately. For example, if the ServletAction.getAction() == 'add', processRequest() should automatically call addCandidate() in TestController. But I am not able to get the value of the Annotation. Somehow annotation.value() is giving a compilation error in eclipse. Eclipse is not showing any method I can use to get the annotation value.

I want to know if there is a way to get value() of the Custom Annotation. I dont want to define my Annotation with anything else other than String value(). I want to know if it is possible to achieve what I want with just String value() in my custom Annotation?

Any help is greatly appreciated. Thanks.

zookastos
  • 795
  • 7
  • 31
  • why does the abstract class have constructor? – Sharon Ben Asher Apr 03 '18 at 19:53
  • Its my bad. This code is work in progress. I thought that I might need some processing done while initializing, but it can be removed if not needed. https://stackoverflow.com/questions/260666/can-an-abstract-class-have-a-constructor – zookastos Apr 03 '18 at 20:09

1 Answers1

3

You probably need to change

Annotation annotation = (ControllerAction) method.getAnnotation(ControllerAction.class);

To

ControllerAction annotation = method.getAnnotation(ControllerAction.class);

Otherwise the methods specific to ControllerAction will not be known to the compiler as annotation is of type Annotation

Additionally - as pointed out by Sharon Ben Asher - instead of AbstractController.class you should use getClass() to get the class of the actual implementation at runtime. Given the current code only the methods of AbstractController will be checked but not the ones of implementing classes.

dpr
  • 8,675
  • 3
  • 32
  • 57
  • 3
    Why do you need to cast? This `ControllerAction annotation = method.getAnnotation(ControllerAction.class);` should be enough. – ikos23 Apr 03 '18 at 19:53
  • I have another question. How is above code efficiency wise? What If I have a lot of methods? Is Java reflection suggested where efficiency a concern? – zookastos Apr 03 '18 at 19:54
  • @john: That is needed, so that compiler know the type and calls appropriate methods instead. The arument on RHS will just give an object of type ControllerAction, which is also of type Annotation. – zookastos Apr 03 '18 at 19:55
  • 1
    ok, this is how `getAnnotation()` looks like inside : `return annotationClass.cast(declaredAnnotations().get(annotationClass));` the casting is already there. I had to check sources for that, so again : why do you need to cast ?) – ikos23 Apr 03 '18 at 19:57
  • 1
    I have to say I do not understand how this works for you. The code examines the methods of `AbstractController` so how is it able to detect a method from a subclass ?? – Sharon Ben Asher Apr 03 '18 at 20:00
  • [`getAnnotation`](https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/Method.html#getAnnotation-java.lang.Class-) is a generic method, so what @john is saying is right. The cast is pointless. – Radiodef Apr 03 '18 at 20:11
  • Of course the cast is not needed due to the greatness of Java‘s generics. Additionally instead of `AbstractController.class` simply `getClass()` should be used to get the class of the actual implementation. – dpr Apr 03 '18 at 20:15
  • @SharonBenAsher you’re absolutely right. I only spotted the obvious error in the casting first. I updated the answer accordingly – dpr Apr 03 '18 at 20:20
  • @dpr: Okay. I am not so godd with Reflection. So how do I make sure that the methods of only implementing subclass are checked. – zookastos Apr 03 '18 at 20:39
  • @john: Agreed. I was wrong earlier. No need for another casting. I thought you were talking about LHS of assignment operator. My bad. – zookastos Apr 03 '18 at 20:40
  • @SharonBenAsher: Okay, so I am not so good with reflection. Can you please tell me how I would check methods of implementing subclasses? Idea is that concrete classes will be using the annotation. Abstract class "processRequest" will call appropriate method according to value of annotation. – zookastos Apr 03 '18 at 20:43
  • 1
    just use `this.getClass()` the current object is of type of the concrete implementation – Sharon Ben Asher Apr 04 '18 at 05:05