2013-11-04 6 views
12

Ich versuche, eine einfache Jersey-Anwendung zu schreiben, die Dateien von einem Jersey-Client zu einem Jersey-Server und zurück sendet. Die Dateien scheinen jedoch nur auf dem Weg vom Client zum Server kodiert zu sein, aber nicht umgekehrt. Ich frage mich, wie ich dieses Verhalten ändern kann.Wie man Jersey verwendet GZip Kompression für die Antwort Nachricht Körper

Ich prüfe dies in einem einfachen Beispiel:

public class GZipEncodingTest extends JerseyTest { 

    private static final String PATH = "/"; 
    private static final String QUESTION = "foo", ANSWER = "bar"; 
    private static final String ENCODING_GZIP = "gzip"; 

    @Path(PATH) 
    public static class MyResource { 
    @POST 
    public Response handle(String question) throws IOException { 
     assertEquals(QUESTION, question); 
     return Response.ok(ANSWER).build(); // (1) 
    } 
    } 

    @Override 
    protected Application configure() { 
    enable(TestProperties.LOG_TRAFFIC); 
    enable(TestProperties.DUMP_ENTITY); 
    return new ResourceConfig(MyResource.class, GZipEncoder.class); 
    } 

    @Override 
    @SuppressWarnings("unchecked") 
    protected void configureClient(ClientConfig config) { 
    config.register(new EncodingFeature(ENCODING_GZIP, GZipEncoder.class)); 
    } 

    @Test 
    public void testHeaders() throws Exception { 
    Response response = target().path(PATH).request().post(Entity.text(QUESTION)); 
    assertEquals(ANSWER, response.readEntity(String.class)); 
    } 
} 

Vom angemeldet dump, kann ich sagen, dass die Anforderung, wie beabsichtigt: die Inhaltscodierung wird in der Kopfzeile und angewendet auf die Anforderungsnachricht Körper signalisiert . Das Accept-Encoding wird ebenfalls festgelegt. Der Server versteht die angewendete gzip-Komprimierung und entpackt den Nachrichtentext der Anfrage. Es ignoriert jedoch die Tatsache, dass der Client eine gezippte Antwort akzeptiert und den Antwortnachrichtentext unkomprimiert sendet.

Als ich encoding(ENCODING_GZIP) in Zeile (1) in der Response -builder Kette anhängen, erhalte ich das Ergebnis die ich suche. Ich möchte jedoch nur die Codierung anwenden, wenn sie in der Anfrage als akzeptabel markiert wurde. Darüber hinaus möchte ich diese Feature-Anwendung breit und nicht nur für spezifische Antworten verbünden.

Ich kann natürlich hinzufügen, ein solches Feature manuell mit einem WriterInterceptor:

public class GZipWriterInterceptor implements WriterInterceptor { 
    @Override 
    public void aroundWriteTo(WriterInterceptorContext context) 
     throws IOException, WebApplicationException { 
    context.getHeaders().add(HttpHeaders.CONTENT_ENCODING, ENCODING_GZIP); 
    context.proceed(); 
    } 
} 

aber ich bin überzeugt, dass dies nicht notwendig Kesselblech.

Die EncodingFeature scheint nur ein Teil der Client-Bibliothek zu sein. Ich bin grundsätzlich auf der Suche nach einer Möglichkeit, den Jersey-Server Daten als gzip codieren, wenn die Anfrage die Codierung über Akzeptieren-Codierung vorgeschlagen.

Wenn ich versuche, nach Lösungen im Internet zu suchen, finde ich reichlich. Die meisten von ihnen betreffen Jersey 1. Einige von ihnen schlagen vor, dem GrizzlyServer einen Listener hinzuzufügen (was Jersey-spezifisch wäre und nicht JAX-RS?). Dann gibt es viele Klassen innerhalb der Jersey 2 Abhängigkeitsbaum, die GZIP-Kodierung vorschlagen:

  • org.glassfish.grizzly.http.GZipContentEncoding
  • org.glassfish.jersey.message.GZipEncoder
  • org.glassfish.grizzly.compression.zip.GZipEncoder
  • org.glassfish.grizzly.compression.zip.GZipDecoder
  • org.glassfish.grizzly.compression.zip.GZipFilter

ich, dass die Menschen gefunden auf das Netz schlägt vor, irgendwelche von ihnen sogar zu verwenden Ich denke, dass org.glassfish.jersey scheint die richtige Wahl zu sein, da es eine tatsächliche Jersey-Abhängigkeit ist. Ganz zu schweigen von denen, die in den ApacheConnector verwandten Bibliotheken zu finden sind. Ich habe keine Ahnung, welchen ich eigentlich benutzen sollte.

+0

Siehe http://stackoverflow.com/questions/19751014/gzip-encoding-in-jersey-2-grizzly –

+0

Nun, mein Problem ist eigentlich ein wenig komplexer. Ich wollte die gzip-Codierung neu erstellen. Ich habe meine Frage hier wiederholt: http://stackoverflow.com/questions/19794014/why-does-jersey-swallow-my-content-encoding-header –

+0

Ich versuche immer noch, die * offizielle Lösung * zu diesem Problem wieder aufzubauen, indem ich bekomme Das GZip-Beispiel funktioniert. Aber ich arbeite nur in einer Richtung, aber nicht in der anderen. Die Lösung, die Sie vorschlagen, sieht für mich wie ein Hack aus, um ehrlich zu sein. –

Antwort

16

Ich fand es heraus, indem ich durch die Jersey-Bibliothek schaute. Für die Server-Seite, ist die folgende Konfiguration notwendig:

@Override 
@SuppressWarnings("unchecked") 
protected Application configure() { 
    ResourceConfig resourceConfig = new ResourceConfig(MyResource.class); 
    EncodingFilter.enableFor(resourceConfig, GZipEncoder.class); 
    return resourceConfig; 
} 

Unter dem convers, EncodingFilter#enableFor(ResourceConfig.Class<? extends ContentEncoder>[]) registriert ein EncodingFilter und die angegebenen GZipEncoder mit den gegebenen ResourceConfig.

Ich denke, der Grund für diesen Umweg in der Registrierung liegt in der Tatsache, dass jede Codierung in zwei Stufen geschehen muss. Zuerst die EncodingFilter (die eine tatsächliche ContainerResponseFilter ändert die Kopfzeile der Antwort durch Einstellung Content-Encoding zu gzip. Zur gleichen Zeit kann ein Filter den Entity-Stream des Nachrichtentextes nicht ändern, da der Filter aufgerufen wird, bevor dieser Stream überhaupt erstellt wird. daher muss der Strom Modifikation von einem WriterInterceptor verarbeitet werden, die nach der Verarbeitung des Filters und auch nach der Gründung des Unternehmens Strom ausgelöst wird.

aus diesem Grund nur die GZipEncoder Registrierung wird für die Anforderung Dekodierung arbeiten, wenn die Header wird auf gzip durch den Client festgelegt, der unabhängig von der Erstellung des Servers auftritt

Das Beispiel, das ich mit meinem 'GZipWriterInterceptor' angegeben habe, ist im Grunde eine schlecht implementierte Version des EncodingFilter. Natürlich sollte der Header in einem Filter und nicht in einem Interceptor gesetzt werden. Es says in the documentation:

Während Filter erster Linie Anfrage und Antwortparameter wie HTTP-Header, URIs und/oder HTTP-Methoden bestimmt sind, zu manipulieren interceptors sollen Entitäten manipulieren, durch Manipulieren Entität Eingabe-/Ausgabeströme

Daher kann Gzip Encoding nicht einfach durch Registrierung eines GZipEncoder aktiviert werden, es muss auch mit einem Filter registriert werden. Deshalb habe ich erwartet, dass beide in einem Feature gebündelt werden.

Wichtig: Es gibt zwei EncodingFilter Klassen in Jersey. Der eine gehört dem Client, der andere der Serverimplementierung. Benutze nicht das Falsche, da sie grundlegend andere Dinge tun. Unglücklicherweise haben Sie beide bei der Ausführung von Komponententests auf Ihrem Klassenpfad, da sie sich auf die Client-Schnittstelle verlassen.

+1

ist es sehr schrecklich ... diese Lösung ist nur für Jersey 2.0+ – Filipe

+0

irgendwelche Zeiger für 1x (1.6)? –