Skip to main content

Jersey 2.0 and Server Sent Events Sample with Glassfish

Posted by bhaktimehta on July 20, 2012 at 10:48 PM PDT

Jersey 2.0 has a lot of new features as mentioned by Marek here .
One of the nice features which they have added is the Client and Server support for Server Sent Events .
Here is a sample which shows how we use the new Server Sent Event apis introduced in jersey 2.0.

In an earlier entry we had shown Server Sent Events sample using the CDI, ServerSentEventHandler and ServerSentEventHandlerContext apis
along with twitter search apis and javascript client code.

The following code is based on similar concept except we will have a Schedule based timer which will periodically poll Twitter for updates.
We will use a JAX-RS resource to send the data using Server Sent Events to a Servlet based client.

Here are some snippets of the code
This is the ServerSentEventsResource.java. As you can see we use the EventChannel as an outgoing event message queue .

@Path("twittersse")
public class ServerSentEventsResource {

       static EventChannel eventChannel= new EventChannel();

       @GET
       @Produces(EventChannel.SERVER_SENT_EVENTS)
       public EventChannel getMessage() {
           return eventChannel;
       }


       @POST
       @Consumes(MediaType.TEXT_PLAIN)
       public void sendMessage(String message) throws IOException {
           System.out.println("Sending " + message);
           eventChannel.write(new OutboundEvent.Builder().name("custom-message").data(String.class, message).build());
       }
}

Here is the code for the Stateless Session bean which will get the data from Twitter and post to the ServerSentEventsResource class every 10 seconds.

@Stateless
@Named
public class TwitterBean {

   private final static String SEARCH_URL =
              "http://search.twitter.com/search.json?q=glassfish&rpp=5&include_entities=true" +
                      "&with_twitter_user_id=true&result_type=mixed";


    private final static String TARGET_URI= "http://localhost:8080/jersey-sse-twitter-sample";

    @Schedule(hour="*", minute="*", second="*/10")
    public void sendTweets() {

        Client client = ClientFactory.newClient();
        try {
            WebTarget webTarget= client.target(new URI(TARGET_URI)) ;

            String message = getFeedData();
            System.out.println("Posting message");
            webTarget.path("twittersse").request().post(Entity.text(message));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

Here is the code for the Servlet client which uses the EventSource apis to listen for the Server Sent Events and prints the information.
When EventSource is created, it makes GET request to given URI and waits for incoming InboundEvents.
Whenever any event is received, onEvent(InboundEvent) is called and listeners are notified
I use the async support in Servlet 3.0.
Also I am using json-lib apis to bind from JSON to Java.

@WebServlet(name = "TestClient", urlPatterns = {"/TestClient"} ,asyncSupported=true)
public class TestClient extends HttpServlet {

    private final static String TARGET_URI = "http://localhost:8080/jersey-sse-twitter-sample/twittersse";
    private ExecutorService executorService;

    @Override
    public void init() throws ServletException {
        executorService = Executors.newFixedThreadPool(3);

    }

  
    @Override
    protected void service(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        try {

            final AsyncContext asyncContext = request.startAsync();
            asyncContext.setTimeout(600000);
            asyncContext.addListener(new AsyncListener() {

                @Override
                public void onComplete(AsyncEvent event) throws IOException {
                }

                @Override
                public void onTimeout(AsyncEvent event) throws IOException {
                    System.out.println("Timeout" + event.toString());
                }

                @Override
                public void onError(AsyncEvent event) throws IOException {
                    System.out.println("Error" + event.toString());
                }

                @Override
                public void onStartAsync(AsyncEvent event) throws IOException {
                }
            });


            Thread t = new Thread(new AsyncRequestProcessor(asyncContext));
            t.start();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * This class will create the EventSource
     * and when the SSE are received will print the data
     * from the Inbound events
     */
    class AsyncRequestProcessor implements Runnable {

        private final AsyncContext context;

        public AsyncRequestProcessor(AsyncContext context) {
            this.context = context;
        }

        @Override
        public void run() {
            Client client = ClientFactory.newClient();
            client.configuration().register(OutboundEventWriter.class);
            context.getResponse().setContentType("text/html");
            final javax.ws.rs.client.WebTarget webTarget;
            try {
                final PrintWriter out = context.getResponse().getWriter();
                webTarget = client.target(new URI(TARGET_URI));
               
                out.println("Glassfish tweets");
               

                EventSource eventSource = new EventSource(webTarget, executorService) {
                    @Override
                    public void onEvent(InboundEvent inboundEvent) {
                        try {
                            //get the JSON data and parse it
                            JSONObject jsonObject = JSONObject.fromObject( inboundEvent.getData(String.class,
                                    MediaType.APPLICATION_JSON_TYPE ));
                            JSONArray jsonArray = (((JSONArray) (jsonObject.get("results"))));
                            for (int i = 0; i                                 JSONObject o = ((JSONObject)jsonArray.get(i)) ;
                                out.println( o.get("text"));
                                out.println("
");
                                out.println("Created at " + o.get("created_at"));
                                out.println("
");

                            }
                            out.println("

");
                            out.flush();

                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                };
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

Here is the Application class to register the JAX-RS resource ServerSentEventsResource

public class MyApplication extends Application {

    Set> classes = new HashSet>() {
        { add(ServerSentEventsResource.class);
            add(OutboundEventWriter.class);
        }};

    @Override
    public Set> getClasses() {
        return classes;
    }
}

Here is the source code to try.
You can build the maven project, download the latest glassfish bits from here and deploy the jersey-sse-twitter-sample.war
You can see at http://localhost:8080/jersey-sse-twitter-sample/TestClient the Twitter updates start showing up.

Note that this is a work in progress so apis may change.You can check for more Jersey related updates here and follow @gf_jersey on Twitter to get Jersey-related news.

Last but not the least I would like to thank my very talented colleagues and friends Jakub Podlesak, Masoud Kalali, Rajiv Mordani and Jitendra Kotamraju for all their wonderful ideas and help. Thanks a lot!

AttachmentSize
jersey-sse-twitter-sample.zip14.72 KB
Related Topics >>