Skip to content

Instantly share code, notes, and snippets.

@danbev
Created November 12, 2012 08:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danbev/4058175 to your computer and use it in GitHub Desktop.
Save danbev/4058175 to your computer and use it in GitHub Desktop.
Route interceptors and decorators

Route CDI Interceptors and Decorators

After a discussion with qmx and Bruno there was a suggestion to have some sort for filter pattern for AeroGear-Controller. This gist describes a suggestion in hope to "buy" some time to implement this.

Suggestion

CDI has the concept of decorators and interceptors which might be a nice fit for our use case. The idea would be to enable us to specify either/both interceptors/decorators to implement things like Security, Error handling, CORS support etc.

As an example, we have done some prototyping and the following section describes how decorators could be used. There are two cases that we have identified so far:

  1. The decorator only needs to inspect/interact with HttpServletRequest and HttpServletResponse
  2. The decorator need access to the Route to perform an action before or after invoking the target Route

Route Decorator

This type of decorator would handle the first type described above where the decorator only needs access to the Request/Response. An example of this could be a CORS decorator which only needs to inspect the request and use the response to set headers. The following in an example of what such a CorsDecorator:

@Decorator
public class CorsHandler implements Router {
    
    @Inject @Delegate @Any
    private Router delegate;
    
    public CorsHandler() {
    }
    
    @Override
    public boolean hasRouteFor(HttpServletRequest httpServletRequest) {
        return delegate.hasRouteFor(httpServletRequest);
    }

    @Override
    public void dispatch(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException {
        // check for cors support and only dispatch if valid
        delegate.dispatch(request, response, chain);
    }

}

Notice that this decorator is decorating at the Router level, before dispatching to the Router.

RouteProcessor Decorator

A RouteProcessor decorator handles the second point above where the decorator requires access to the target Route. For this, RouteProcessor is a new interfaces introduced to give an additional point for decorating which is a very simple interface:

public interface RouteProcessor {
    
    public void process(Route route, RouteContext routeContext) throws Exception;

}

RouteContext just contains the requestPath, the HttpServletRequest/HttpServletResponse, the current Route, and the Routes instance. This was just thrown together without out much thought and should be looked at more closely if this is determined as a valid solution.

With this in place we can extract the error handling to an ErrorHandler decorator.
The security code can be extracted to a SecurityHandler, for example:

@Decorator
public class SecurityHandler implements RouteProcessor {
    
    @Inject @Delegate @Any
    private RouteProcessor delegate;
    private SecurityProvider securityProvider;
    
    @Inject
    public SecurityHandler(final SecurityProvider securityProvider) {
        this.securityProvider = securityProvider;
    }

    @Override
    public void process(Route route, RouteContext routeContext) throws Exception {
        if (route.isSecured()) {
            System.out.println("[SecurityHandler{"+ securityProvider.getClass().getName() + "}] checking security");
            securityProvider.isRouteAllowed(route);
        }
        delegate.process(route, routeContext);
    }
    
}

The actual SecurityProvider is still injected and as configurable as before but is now not as tightly tied to the routing code.

Configuring these decorators is done by adding them to the decorators section of beans.xml:

<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

    <alternatives>
        <class>org.jboss.aerogear.controller.spi.DummySecurityProvider</class>
    </alternatives>
    <decorators>
        <class>org.jboss.aerogear.controller.router.decorator.CorsHandler</class>
        <class>org.jboss.aerogear.controller.router.decorator.ErrorHandler</class>
        <class>org.jboss.aerogear.controller.router.decorator.SecurityHandler</class>
    </decorators>
</beans>

An additional plus of this is that we have control of the order of decorators which is something we don't currently have when it comes to ServletFilters. For example, for CORS, if we wanted to implement it as a ServletFilter we would require it to be the first filter executed so that all checks are done up front before the AeroGear filter processes the request. ServletFilter order can only be done by specifying the order in web.xml and when using annotations there are no order guarantees.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment