AOP aware Madvoc action requests

This is a story from Madvoc-Spring-Hibernate real-world project. In this project, on some Madvoc actions Spring applied one or more aspects, using CGLIB. Therefore, when Madvoc creates ActionRequest, it contains a proxy class of the real action.

Why this is a problem? Because of interceptors and parameter/attributes injection. If you are using Madvoc in a proposed way, actions will have just fields annotated with @In and @Out annotations, and not getters and setters methods - just for the sake of simplicity and less code. However, it is impossible to inject fields value in CGLIB proxy simply because they are not there: CGLIB proxy offers interface methods of adviced target. Note that this would not be a case if Proxetta is used instead of CGLIB;)

Solution is not hard: when new ActionRequest is created, we will detect if corresponding action object is a proxy or not. So here we have two tasks; first to instantiate custom ActionRequest and then to work-around CGLIB proxy.

Custom action requests

This one is trivial: MadvocController has method createActionRequest() that can be overridden:

    public class FooMadvocController extends MadvocController {

        @Override
        protected ActionRequest createActionRequest(
                ActionConfig actionConfig, Object action,
                HttpServletRequest servletRequest,
                HttpServletResponse servletResponse) {

            return new FooActionRequest(
                actionConfig, action, servletRequest, servletResponse);
        }
    }

Here we decide what ActionRequest instance is going to be created and used.

Handling CGLIB proxy

The second step is to detect if action is a proxy and to find a reference to original action (proxy target). Here is one simple solution:

    public class FooActionRequest extends ActionRequest {

        Object proxy;
        Object target;

        /**
         * Detects CGLIB proxy created by Spring.
         * Stores proxy instance and replace advised target
         * before interceptors are invoked.
         */
        public PectopahActionRequest(
                ActionConfig config, Object action,
                HttpServletRequest serlvetRequest,
                HttpServletResponse servletResponse) {
            super(config, action, serlvetRequest, servletResponse);
            if (AopUtils.isCglibProxy(action)) {
                proxy = action;
                try {
                    this.target = this.action =
                        ((Advised) action).getTargetSource().getTarget();
                } catch (Exception ex) {
                    throw new PectopahException("Invalid proxy.");
                }
            }
        }

        /**
         * After interceptors are invoked, replace back proxy instance.
         */
        @Override
        protected Object invokeAction() throws Exception {
            if (proxy != null) {
                action = proxy;
            }
            Object result = super.invokeAction();
            if (proxy != null) {
                action = target;
            }
            return result;
        }
    }

In constructor we check if action is a proxy, and if that is a case, we get the reference to the target action (the 'real' action, not the CGLIB proxy). Then, we will store the proxy instance, and use the real instance of the action. The consequence is that all interceptors will not see the proxy, and therefore, they will be able to inject into fields.

Later, while action method is about to be invoked (#invokeAction), we simply do the switch and place the proxy as an action.