JAX-RS - Dynamic Filter Binding

[Updated: Nov 22, 2017, Created: Jul 17, 2017]

Dynamic binding is a way to bind filters to resource methods programmatically.

Name binding uses a static approach. With dynamic binding we can implement code which defines the bindings during the application initialization time.

To achieve dynamic binding, we need to define a feature (extending a class from DynamicFeature and annotate it with @Provider) and programmatically register our filter (the Filter should not use @Provider).

Let's see an example how to dynamically bind a filter to resources.

Example

Implementing Filter

public class LogFilter implements ContainerRequestFilter, ContainerResponseFilter {

  @Override
  public void filter(ContainerRequestContext reqContext) throws IOException {
      System.out.println("-- req info --");
      log(reqContext.getUriInfo(), reqContext.getHeaders());

  }

  @Override
  public void filter(ContainerRequestContext reqContext,
                     ContainerResponseContext resContext) throws IOException {
      System.out.println("-- res info --");
      log(reqContext.getUriInfo(), resContext.getHeaders());


  }

  private void log(UriInfo uriInfo, MultivaluedMap<String, ?> headers) {
      System.out.println("Path: " + uriInfo.getPath());
      headers.entrySet().forEach(h -> System.out.println(h.getKey() + ": " + h.getValue()));
  }
}

Implementing DynamicFeature

@Provider
public class FilterRegistrationFeature implements DynamicFeature {
  @Override
  public void configure(ResourceInfo resourceInfo, FeatureContext context) {
      if (MyResource.class.isAssignableFrom(resourceInfo.getResourceClass())) {
          Method method = resourceInfo.getResourceMethod();
          if (method.getName().toLowerCase().contains("order")) {
              System.out.println("registering LogFilter");
              context.register(new LogFilter());
          }
      }
  }
}

A JAX-RS resource

@Path("")
public class MyResource {

  @GET
  @Path("customers")
  public String getAllCustomers() {
      System.out.println("in getAllCustomers()");
      return "dummy-response for all customers";
  }

  @GET
  @Path("customers/{id}")
  public String getCustomerById(@PathParam("id") String id) {
      System.out.println("in getCustomerById()");
      return "dummy-response for customer " + id;
  }

  @GET
  @Path("orders")
  public String getOrders() {
      System.out.println("in getOrders()");
      return "dummy-response for orders";
  }
}

To try examples, run embedded tomcat (configured in pom.xml of example project below):

mvn tomcat7:run-war

Output

During startup on server console:

registering LogFilter

Using HTTPie to access the resource:

Server output:

in getAllCustomers()

Server output:

-- req info --
Path: orders
host: [localhost:8080]
connection: [keep-alive]
accept-encoding: [gzip, deflate]
accept: [*/*]
user-agent: [HTTPie/0.9.9]
in getOrders()
-- res info --
Path: orders
Content-Type: [text/plain]

As seen in above outputs, our LogFilter was only executed for '/orders' (that is what we dynamically registered our LogFilter with)

Example Project

Dependencies and Technologies Used :

  • jersey-server 2.25.1: Jersey core server implementation.
  • jersey-container-servlet 2.25.1: Jersey core Servlet 3.x implementation.
  • JDK 1.8
  • Maven 3.3.9

JAX-RS Filter Example Select All Download
  • filter-registration-with-dynamic-feature
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also