Pages

Friday, 13 February 2015

Decoding GZIP compressed data using Interceptors & Filters in Jersey 2.x clients

Usecase:

Previously, we created a service that handles gzip encoding of data. Refer this for details. In this blogpost, I will show you how you can decode the gzip encoded data. Be sure to use Jersey 2.x. Details on interceptors and filters can be found here.

Pre-requisites:

JDeveloper 12.1.3.0.0

Steps:
Create a new custom application and invoke the RESTful Client and Proxy wizard. Supply the WADL URL for the previously created service and generate a client.
To decode the gzip encoded data, at the client end, we need to use a 'ReaderInterceptor'.
Let us first create a class 'GZipReaderInterceptor' which implements 'ReaderInterceptor'. In this class, write the below code. Notice the @Provider annotation at the class level.

package project1;

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
@Provider
public class GZipReaderInterceptor implements ReaderInterceptor {
    public GZipReaderInterceptor() {
        super();
    }

    @Override
    public Object aroundReadFrom(ReaderInterceptorContext readerInterceptorContext) throws IOException,WebApplicationException {
        // TODO Implement this method
        final InputStream originalInputStream = readerInterceptorContext.getInputStream();
        readerInterceptorContext.setInputStream(new GZIPInputStream(originalInputStream));
        return readerInterceptorContext.proceed();
    }
}

In the main client class, write the code to invoke the service, something like below in my case:

public class empClientClient {
    public static void main(String[] args) {
        Client client = empClient.createClient();
        empClient.Project1 empclientproject1 = empClient.project1(client);
        // add your code here
        System.out.println(empclientproject1.getAsXml(String.class));
    }
}
Also, in the auto generated code for you (on creating the client), go to the class which contains all the logic to invoke service operations. Find the method 'customizeClientConfiguration'. Add the below line in this method:

    private static void customizeClientConfiguration(Configurable cc) {
        cc.register(GZipReaderInterceptor.class);
    }
At this point, if you run the client, you will end up with below exception:

  Exception in thread "main" javax.ws.rs.ProcessingException: Error reading entity from input stream.
 at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:868)
 at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:785)
 at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:267)
 at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
 at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
 at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
 at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
 at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:397)
 at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
 at project1.empClient$Project1.getAsXml(empClient.java:295)
 at project1.empClientClient.main(empClientClient.java:15)
Caused by: java.util.zip.ZipException: Not in GZIP format
 at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:164)
 at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:78)
 at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:90)
 at project1.GZipReaderInterceptor.aroundReadFrom(GZipReaderInterceptor.java:24)
 

This is because the service expects the client to send a header 'Accept-Encoding'. So, let us add this header to the client requests. This header needs to be added before the request is sent to the server. For this, we will use a Pre Matching Filter. Create a new java class which implements 'ClientRequestFilter'. Add the below code into it. Notice that we have added the annotation @PreMatching.

package project1;

import java.io.IOException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.container.PreMatching;

@PreMatching
public class PreRequestFilter implements ClientRequestFilter {
    public PreRequestFilter() {
        super();
    }

    public void filter(ClientRequestContext requestContext) throws IOException {
        requestContext.getHeaders().add("Accept-Encoding", "gzip");
            }
}

Also, go back to the method 'customizeClientConfiguration' and add another line in it to register this filter. The modified method now looks as below:
 private static void customizeClientConfiguration(Configurable cc) {
        cc.register(PreRequestFilter.class);
        cc.register(GZipReaderInterceptor.class);
    }
Now, run the client. You should be able to see the decoded response in the console.



If you see the corresponding request in analyzer, you should be able to see the header 'Accept-Encoding' as well as the response.



Friday, 6 February 2015

Performing GZIP compression in JAXRS 2.0 services using Interceptors

Usecase:

This blogpost talks about using GZip compression on a REST service through the use of JAXRS 2.0 interceptors. The usecase below uses Jersey 2.x. Details on interceptors and filters can be found here.

Pre-requisites:

JDeveloper 12.1.3.0.0

Steps:
Use a working REST service that you already have and want to use GZip compression on. Alternately, create a REST service from scratch using the steps mentioned in my previous blogpost. However, ensure that you are using Jersey 2.0 libraries.

Next, create a new java class that implements ‘WriterInterceptor’. Write the below code in it. Notice that we have an annotation @ Provider at the class level.
We are also checking if the client accepts encoding of 'gzip' format, and only then performing the gzip compression.
  1. package project1;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.zip.GZIPOutputStream;
    import javax.ws.rs.WebApplicationException;
    import javax.ws.rs.core.Context;
    import javax.ws.rs.core.HttpHeaders;
    import javax.ws.rs.ext.Provider;
    import javax.ws.rs.ext.WriterInterceptor;
    import javax.ws.rs.ext.WriterInterceptorContext;
    
    @Provider
    public class GZIPWriterInterceptor implements WriterInterceptor {
        public GZIPWriterInterceptor() {
            super();
        }
        private HttpHeaders context;
    
        public GZIPWriterInterceptor(@Context HttpHeaders context) {
            this.context = context;
        }
    
        @Override
        public void aroundWriteTo(WriterInterceptorContext writerInterceptorContext) throws IOException,WebApplicationException {
            // TODO Implement this method
            String acceptEncoding = context.getHeaderString("Accept-Encoding");
            if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
                final OutputStream outputStream = writerInterceptorContext.getOutputStream();
                writerInterceptorContext.setOutputStream(new GZIPOutputStream(outputStream));
                writerInterceptorContext.getHeaders().putSingle("Content-Encoding", "gzip");
            }
            writerInterceptorContext.proceed();
    
        }
    }
Run the service class. Click on the target URL to open the HTTP analyzer and send request. Since, we have not defined the 'Accept-Encoding' header, the compression will not occur. A normal request- response cycle happens as below:



Next, add the header 'Accept-Encoding' to the request and send again. This time, gzip compression takes place. Notice that the size of data has now reduced to 123 from 155.



Find my sample application here.
Side note: The sample application also uses the concept of name binding by creating a custom annotation @Compressor and attaching it to the GZipWriterInterceptor class. Additionally, the annotation is specified on the methods for which GZip compression is required. This is not required, but comes in handy if you want to apply compression on some methods, and leave out the rest uncompressed.

Do check my next post which talks about decoding the gzip encoded data in a java client.

Tuesday, 3 February 2015

Consuming a web socket service through java proxy

Usecase:

This is in continuation to my previous blogpost which talked about creating a web socket service. Here, I will show you how the web socket service can be consumed through a java client.

Pre-requisites:

JDeveloper 12.1.3.0.0

Steps:
  • Create a new custom application. In Step 2 of the wizard, select the project feature as 'Web Socket' and toggle it to the right. You will see Web Socket as well as java listed out in the RHS. Finish the wizard.
  • Create two java classes ClientClass.java and MainClass.java. Keep them in package 'websocketclient'. Write the following code in MainClass.java
  1. package websocketclient;
    
    import java.io.IOException;
    import java.net.URI;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    import javax.websocket.ContainerProvider;
    import javax.websocket.DeploymentException;
    import javax.websocket.Session;
    import javax.websocket.WebSocketContainer;
    
    public class MainClass {
        final static CountDownLatch messageLatch = new CountDownLatch(1);
    
        public static void main(String[] args) throws DeploymentException, IOException {
            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            String uri = "ws://127.0.0.1:7101/WebSocketSample-Project1-context-root/service/Andy"; //replace this with the web socket url generated for you
            System.out.println("Connecting to " + uri);
            container.connectToServer(ClientClass.class, URI.create(uri));
            try {
                System.out.println("Waiting");
                messageLatch.await(100, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
  • In ClientClass.java, write the below code:
  • package websocketclient;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Map;
    import javax.websocket.ClientEndpoint;
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    
    public class ClientClass {
        public void onOpen(Session session) {
            System.out.println("Connected to endpoint: " + session.getBasicRemote());
            try {
                String msg = "Hello! How are you?";
                System.out.println("Sending message to endpoint: " + msg);
                session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                System.out.println(e);
            }
        }
    
        public void onMssage(String input, Session session) {
            System.out.println("Received message in client: " + input);
            MainClass.messageLatch.countDown();
            try {
                session.close();
            } catch (IOException e) {
            }
        }
    
        public void onCls(Session session) {
            System.out.println("Closing");
            try {
                session.close();
            } catch (IOException e) {
            }
        }
    }
  • In ClientClass.java, focus on the class name and through PI, select Web Socket Client.

  • Focus on onOpen method and through PI, select onOpen Method. Focus on onMssage method and through PI, select onMessage Method. Focus on onCls method and through PI, select onClose Method.
  • package websocketclient;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Map;
    import javax.websocket.ClientEndpoint;
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    
    @ClientEndpoint
    public class ClientClass {
        @OnOpen
        public void onOpen(Session session) {
            System.out.println("Connected to endpoint: " + session.getBasicRemote());
            try {
                String msg = "Hello! How are you?";
                System.out.println("Sending message to endpoint: " + msg);
                session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                System.out.println(e);
            }
        }
    
        @OnMessage
        public void onMssage(String input, Session session) {
            System.out.println("Received message in client: " + input);
            MainClass.messageLatch.countDown();
            try {
                session.close();
            } catch (IOException e) {
            }
        }
    
        @OnClose
        public void onCls(Session session) {
            System.out.println("Closing");
            try {
                session.close();
            } catch (IOException e) {
            }
        }
    }
    
  • Run MainClass.java