3

I have an annotation called "@ProgressCheck" that we can put on a controller to check the progress of an application. If the application is already submitted or late, then it throws the user to a page appropriate for that situation.

The annotation interface is:

@Retention(RetentionPolicy.RUNTIME)
public @interface ProgressCheck {
}

The "implementation" of that annotation is something like:

@Around("execution(* *(..)) && args(session,..) && @annotation(com.foo.aspects.progress.ProgressCheck)")
public Object progressCheck(ProceedingJoinPoint pjp, HttpSession session) throws Throwable {

    Applicant applicant = this.applicationSessionUtils.getApplicant(session);
    Application application = this.applicationSessionUtils.getApplication(session);

    switch (this.applicantService.getProgress(applicant)) {
    case SUBMITTED:
        return this.modelAndViewFactory.gotoStatusPage(applicant, application);

    case LATE:
        return this.modelAndViewFactory.gotoDeadlineMissed(applicant, application);

    default:
    case IN_PROGRESS:
        return pjp.proceed();
    }
}

Based on values in the session and database, the annotation's "implementation" allows the user to proceed into a controller, or redirects them to another ModelAndView as appropriate.

I'd like to provide parameters to the annotation and then, in this "implementation" logic, use those parameters to further tune the decision. How, from this logic, without knowing where the annotation is applied, do I access those parameters? Or is there another approach I should be using?

Marvo
  • 17,845
  • 8
  • 50
  • 74
  • 1
    Using the accessor method defined in the annotation? – biziclop May 27 '15 at 19:47
  • I don't think the other question is the same as what I'm asking. If I'm not mistaken, that question deals with a method that has been annotated, and obtaining the value assigned to the annotation in that specific instance. I want to write an annotation that accepts parameters and implement the annotation to do something based on those parameters. – Marvo May 27 '15 at 19:50
  • 2
    What do you mean _implement the annotation to do something_? An annotation doesn't do anything. It is just metadata. There are annotation processors that perform the logic based on that metadata. The duplicate explains how to get that metadata. That is, it explains how to invoke `dayNames` from an instance of type `Day` retrieved from some annotated member (ex. a method). If that isn't your question, please clarify. – Sotirios Delimanolis May 27 '15 at 19:53
  • @biziclop: Sorry, but this is new to me. I don't know what method you're talking about. Can you point me at something that explains it or provide an example? – Marvo May 27 '15 at 19:53
  • @Marvo You don't really implement anything in the annotation. It's just a way to associate metadata with various elements. Any business logic using that metadata has to be implemented elsewhere and access the annotation as explained in the other question. – biziclop May 27 '15 at 19:53
  • Here's the basic [annotation tutorial](https://docs.oracle.com/javase/tutorial/java/annotations/). – biziclop May 27 '15 at 19:54
  • @SotiriosDelimanolis For example, (@)Secured allows the flow of the app to go into a method, or not, depending on what roles are specified. Sorry if I don't know the right terminology. That's part of the problem I'm having with doing this. (I'll investigate trying to get the info from the instance of Day.) – Marvo May 27 '15 at 19:55
  • @Marvo So `@Secured` doesn't actually do anything. Spring has some AOP (Aspect Oriented Programming) interceptors that intercept your beans, parse them for annotations, and wrap them in proxies (JDK or CGLIB) that add the "secured" behavior. – Sotirios Delimanolis May 27 '15 at 19:56
  • Ahhh. That makes sense. Thanks. – Marvo May 27 '15 at 20:00
  • 1
    Might want to look at the [`Proxy`](http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html) class. The annotations are a very small part of the `@Secured` behavior's implementation. – Sotirios Delimanolis May 27 '15 at 20:00
  • I'm going to edit to rephrase (and provide an example of what I consider the "implementation" of an annotation.) It may be that it's still not possible to do what I think I want to do, but I'd like to understand further. – Marvo May 27 '15 at 20:03
  • Updated. (Perhaps I should be using the term aspect?) – Marvo May 27 '15 at 20:11
  • @SotiriosDelimanolis Is it at least not a duplicate? – Marvo May 27 '15 at 20:22
  • Reopened. Maybe rephrase the title too. – Sotirios Delimanolis May 27 '15 at 20:55

1 Answers1

0

The trick is to query the aspect's JoinPoint object to get annotation information about the method (or class or whatever) that's annotated. That's the glue I couldn't figure out.

Here's an example. First, the interface of the aspect. This annotation will only be applied to methods.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DayOfWeek {

    public String[] names();

}

Now the implementation of the aspect:

@Aspect
public class DayOfWeekAspect {

    public DayOfWeekAspect() {
    }

    @Around("execution(* *(..)) && args(session,..) && @annotation(edu.berkeley.jazztwo.aspect.ams.roles.DayOfWeek)")
    public Object dayOfWeek(ProceedingJoinPoint joinPoint, HttpSession session) throws Throwable {

        String[] names = this.getNames(this.getAnnotatedMethod(joinPoint));

        for (String name : names) {
            System.out.println("DayOfWeek:   " + name);
        }

        // Do whatever you want with the aspect parameters
        if (checkSomething(names)) {
            // in some cases, go to a different view/controller
            return new ModelAndView(...);
        }

        // Just proceed into the annotated method
        return joinPoint.proceed();
    }

    private Method getAnnotatedMethod(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        return methodSignature.getMethod();
    }

    private String[] getNames(Method method) {
        DayOfWeek aspect = method.getAnnotation(DayOfWeek.class);
        if (aspect != null) {
            return aspect.names();
        }
        return new String[0]; // no annotation, return an empty array
    }
}

Then apply the aspect to some method

@DayOfWeek(names = {"Monday", "Wednesday", "Friday"})
@RequestMapping(value = "foo", method = RequestMethod.GET)
public ModelAndView foo(...) {
    ...
}

Then, if you're like me, you scratch your head and wonder why it doesn't work. You finally remember (if you're using XML configuration) that you have to instantiate the bean somewhere in your configuration:

<bean id="dayOfWeekAspect" class="com.foo.bar.DayOfWeekAspect" />     

THEN you can invoke foo and it should print out Monday, Wednesday, and Friday.

Marvo
  • 17,845
  • 8
  • 50
  • 74