02.02.2013

ContainerRequestFilter und ContainerResponseFilter mit Annotationen für JAX-RS 2.0

In diesem Teil des Tutorials werden Filter so erweitert, dass sie gezielt bei einzelnen Methodenaufrufen der REST-Schnittstelle greifen. Dafür werden eigene Annotationen verwendet, die in der Filterklasse und im Server-Interface ergänzt werden müssen.

Auf der Abbildung sieht man, wie der Client einen Request an den Service /itemService/itemstring des Servers absetzt. Bevor der Request beim REST-Service ankommt, geht er durch einen ContainerRequestFilter. Falls ein Request gefiltert wird, findet eine Umleitung zurück zum Client statt. Bei der Response verhält es sich ähnlich. Bevor die Response an den Client gesendet wird, läuft sich durch einen ContainerResponseFilter. Falls der Inhalt der Response herausgefiltert wird, liefert der REST-Server ein leeres Ergebnis zurück. Ansonsten kommt das Ergebnis Item 1 beim Client an.

Anmerkung: Die Abbildung bezieht sich auf das Beispiel weiter unten. Mit dem ContainerResponseFilter kann auch die Response verändert werden, bevor sie an den Client gesendet wird. Es ist also nicht nur möglich überhaupt kein Ergebnis zu liefern, sondern auch die Antwort zu bearbeiten.

Als erstes erstellen wir eine neue Annotation mit dem Namen @StringLogger im Projekt JAXRSServer.

package org.hameister.itemservice;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.NameBinding;

@Retention(value = RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@NameBinding
public @interface StringLogger { 
}

Zu beachten ist dabei die Annotation @NameBinding aus dem Package javax.ws.rs.NameBinding, die dafür sorgt, dass die Annotation StringLogger später über den Namen zu finden ist.

Als nächstes wird die Klasse TestSpecialFilter im Projekt JAXRSServer angelegt, die die Interfaces ContainerRequestFilter und ContainerResponseFilter implementiert. In den zu implementierenden Methoden wird beim Aufruf ein Text ausgegeben, so dass man sieht, dass der Filter angesprochen wurde.

package org.hameister.itemservice;

import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
@StringLogger
public class TestSpecialFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext crc) throws IOException {
        System.out.println("=== Filter Request String Logger ===");
    }

    @Override
    public void filter(ContainerRequestContext crc, ContainerResponseContext crc1) throws IOException {
        System.out.println("=== Filter Response String Logger ===");
    }   
}

Wichtig ist die aus dem vorherigen Teil des Tutorials bekannte Annotation @Provider, die dafür sorgt, dass der Filter registriert wird. Außerdem wird die "Custom"-Annotation @StringLogger benötigt, damit der Filter über den Namen gefunden werden kann.

Was jetzt noch fehlt, ist die Erweiterung des REST-Services dahingehend, dass beim Aufruf der Methode /itemstring der Filter TestSpecialFilter angesprochen wird. Um das zu erreichen wird einfach die Annotation @StringLogger oberhalb der Methode itemstring hinzugefügt (Zeile 21).

package org.hameister.itemservice;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;

@Path("/itemservice")
public class ItemService {

    @GET
    @Path("itemstring")
    @Produces({"text/plain, application/json, application/xml"})
    @StringLogger
    public String itemstring() {
        return "Item 1";
    }
    
...    
}    

Um den Filter zu testen, müssen nur die JUnit-Tests aus Erstellen von MessageBodyReader und MessageBodyWriter ausgeführt und die Log-Ausgaben beobachtet werden. Es sollten Ausgaben der Form === Filter Response String Logger === erscheinen.

Im nächsten Teil des Tutorials geht es um Interceptoren bei JAX-RS 2.0.