Skip to main content
Last updated May 27, 2011 16:31, by spericas
Feedicon  

Old Proposal (No async support)


Interceptors vs. Filters


So far both terms have been used rather interchangeably using the wrapping and non-wrapping qualifiers. In this proposal, the term *filter* will be reserved for the non-wrapping case and the term *interceptor* will be reserved for the wrapping case. This terminology seems to align better with previous uses of these terms.

This proposal suggests including both interceptors and filters in order to support all the use cases. Assuming ClientRequest and ServerResponse are mutable classes in the core pagkage, interceptors and filters can also be defined as part of the core (and thus available on the server and the client) using the following interfaces:

interface ClientRequestFilter {
  ClientRequest filter(ClientRequest req);
}
interface ServerResponseFilter {
  ServerResponse filter(ServerResponse res);
}

interface Interceptor {
  ServerResponse intercept(InterceptorContext ctx);
}
interface InterceptorContext {
  ClientRequest getRequest();
  ServerResponse proceed() throws Exception;
}

Request filters will be called before any interceptor; response filters will be called after all interceptors. In pseudo-code:

  Request req = ...;
  Response res = ...;
  ResourceMethod m = ...;
  
  for (ClientRequestFilter f : ...) {
    req = f.filter(req);
    if (req == null) abort();
  }

  InterceptorContext ctx = ...;
  Interceptor si = getFirstInterceptor();
  if (si != null) 
    res = si.intercept(ctx);   // interceptor chain
  else 
    res = invokeMethod(m);      // no chain, call method directly

  for (ServerResponseFilter f : ...) {
    res = f.filter(res);
    if (res == null) abort();
  }

Binding


There are different ways in which interceptors and filters can be associated with resource methods and resource classes. The first option is using annotations like those in EJB or in CDI. CDI annotations provide an extra level of indirection and flexibility. Hence, the following proposal for static binding via annotations:

@InterceptorBinding          // Same name used by CDI?
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Logged { 
}
@Logged
public class MyInterceptor implements Interceptor {

  ServerResponse intercept(InterceptorContext ctx) {
    // Log before calling resource method
    res = ctx.proceed();
    // Log after calling resource method
    return res;
  }
}

And similarly for filters:

@FilterBinding 
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Profiled { 
}
@Profiled
public class MyFilter implements ClientRequestFilter {

  private int count;

  public Request filter(Request req) {
    System.out.println(++count);
    return req;
  }    
}

The annotations @Profiled and @Logged defined above can be used on resource methods or resource classes (applying to all methods). For example,

@Logged
public MyResourceClass {

  @Profiled 
  @GET
  @Path({id})
  public Foo getFoo(@PathParam("id") String id) {
    ...
  }
}

In the example above, the request will first be filtered by MyFilter and then intercepted by MyInterceptor, according to the ordering defined above.

There are use cases in which interceptors and/or filters need to be bound to all resource classes in an application. A meta-annotation parameter can be used to define an annotation that can be used to decorate global filters or interceptors. For example,

@InterceptorBinding(global=true)    // A global interceptor
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Logged { 
}

Based on this definition, any interceptor class annotated with @Logged will apply to all resource classes. By default, interceptors and filters are not global.

Filter/Interceptor Order


Ordering among filters and among interceptors can be set using the ordering property on the @InterceptorBinding and @FilterBinding. JAX-RS implementations should document the ordering defined for any filters/interceptors provided out-of-the-box.

@InterceptorBinding(global=true, ordering=20)    // A global interceptor with ordering 20
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Logged { 
}

Interceptors must be ordered based on the ordering property defined by the meta-annotation @InterceptorBinding. Ditto for Filters.

 
 
Close
loading
Please Confirm
Close