Home > Atmosphere, Comet, JQuery, Websocket, Wicket > Writing Websocket applications using Apache Wicket

Writing Websocket applications using Apache Wicket

Writing Apache Wicket application is quite simple. Adding Websocket support to Wicket applications is event simpler! How? By combining the power of the Atmosphere Framework with Wicket, you can quickly transform any existing Wicket application into a powerful HTLM5 application. You think it impossible because your browser or server doesn’t support Websocket? Not a problem with the Atmosphere Framework: both the client and server component are able to emulate Websocket using Comet. What does it means? It means that if you deploy on a Webserver that support Websocket, Atmosphere will use it and if not will use Comet. That means you are 100% guarantee that your Wicket application can be deployed anywhere, and can be used with any browser supporting or not Html5’s Websocket.

Let’s create an extremely simple Wicket application: a simple clock server. The browser will connect to the server and get update about the current time. Nothing complicated, but the goal is to demonstrate how easy it is. Our Web page will look like:

The Server Side

First, let’s just define our web.xml as (all source are available here).


<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         version="2.4">

    <display-name>Wicket+Atmosphere</display-name>

    <servlet>
        <description>MeteorServlet</description>
        <servlet-name>MeteorServlet</servlet-name>
        <servlet-class>org.atmosphere.cpr.MeteorServlet</servlet-class>
        <init-param>
            <param-name>org.atmosphere.filter</param-name>
            <param-value>org.apache.wicket.protocol.http.WicketFilter</param-value>
        </init-param>
        <init-param>
            <param-name>applicationClassName</param-name>
            <param-value>org.atmosphere.samples.wicket.WicketPushApplication</param-value>
        </init-param>
        <init-param>
            <param-name>org.atmosphere.useWebSocket</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>org.atmosphere.useNative</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>filterMappingUrlPattern</param-name>
            <param-value>/*</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>MeteorServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

We configure Atmosphere by telling the framework to use the MeteorServlet, and tell the servlet to run Wicket and make sure all Atmosphere’s object are injected and available from Wicket. Since we want to support Websocket, we also set it there. Finally, we tell Atmosphere to use native Comet implementation when possible, instead of the Servlet 3.0 Async API (which is not available in many server right now anyway). For people already using Atmosphere, note that here we aren’t using the usual AtmosphereServlet because it made it easier for existing Wicket application to use Meteor.

Now I assume you are familiar with Wicket. If not, take a look at their HelloWord sample. First, our HomePage just consist of a Label and a Panel:

The ClockPanel looks like:


public class ClockPanel extends Panel {

    public ClockPanel(String id) {
        super(id);
        add(new BookmarkablePageLink<PushPage>("cometStart", PushPage.class));
        add(new Label("clock", new AbstractReadOnlyModel<String>() {
            @Override
            public String getObject() {
                return new Date().toString();
            }
        }));
    }
}

Atmosphere comes to play inside our Wicket’s WebPage implementation called PushPage


public class PushPage extends WebPage implements AtmosphereResourceEventListener {

    private final AtomicBoolean scheduleStarted = new AtomicBoolean(false);

    public PushPage() {
        HttpServletRequest req = getWebRequestCycle()
               .getWebRequest().getHttpServletRequest();
        Meteor meteor = Meteor.build(req);
        if (!scheduleStarted.getAndSet(true)) {
            meteor.schedule(new Callable<String>() {
                public String call() {
                    String s = new Date().toString();
                    return s;
                }
            }, 1); // One second
        }
        meteor.addListener(this);

        // Depending on the connection
        String transport = req.getHeader("X-Atmosphere-Transport");
        meteor.suspend(-1, !(transport != null
              && transport.equalsIgnoreCase("long-polling")));
    }

    public void onBroadcast(AtmosphereResourceEvent
           <HttpServletRequest, HttpServletResponse> event) {

        String transport = event.getResource()
                .getRequest().getHeader("X-Atmosphere-Transport");
        if (transport != null && transport.equalsIgnoreCase("long-polling")) {
            Meteor meteor = Meteor.lookup(event.getResource().getRequest());

            meteor.removeListener(this);
            meteor.resume();
        }
    }

    ...
}

This is as simple as:

  1. Get a Meteor instance from the current WebRequest
  2. If we haven’t yet scheduled a task using an Atmosphere’s Broadcaster, schedule it. For our application, we just schedule a Callable that return the current date.
  3. Since we want to support all asynchronous techniques, we add an Atmosphere’s listener to get notified every time the clock is updated. This is required in order to support the long-polling technique.
  4. Finally we suspend the current response. Under the hood, Atmosphere will execute the Websocket upgrade, or avoid committing the response if Comet is required
  5. If the long-polling technique is used, the response will be resumed after every broadcast.

So when a browser send a request, we suspend the underlying connection, and that connection will be used every time a broadcast happens, which is every second. That means the client will receive update every second. As an application developer, note that you DON’T HAVE TO LEARN ANYTHING WEBSOCKET/COMET related. The framework is doing it for you so you can focus on the application logic instead of re-inventing the wheel over and over.

The Client Side

Since we are using Wicket, the client side just consist of defining our ClockPanel.html. Since we don’t want to think about Websocket oups (is the browser supports it?, how to use the api? Is the server supports it?), let’s use the Atmosphere JQuery Plugin to do the work for us:


<html
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:wicket="http://wicket.apache.org/.../wicket-xhtml1.4-strict.dtd"
        xml:lang="en"
        lang="en">
<wicket:head>
    <script src="http://github.com/Atmosphere/.../jquery-1.4.2.js"
            type="text/javascript"></script>
    <script src="http://github.com/Atmosphere/.../jquery.atmosphere.js"
            type="text/javascript"></script>
    <script>
       $(document).ready(function() {

            function callback(response) {
                if (response.status == 200) {
                    document.getElementById('clock').innerHTML = response.data;
                }
            }

            // You can set websocket, streaming or long-polling here.
            $.atmosphere.subscribe(document.getElementById('cometStart').href,
                    callback,
                    $.atmosphere.request = {transport: 'websocket'});
        });
    </script>
</wicket:head>
<body>
<wicket:panel>
    <a wicket:id="cometStart" id="cometStart"></a>
    <div wicket:id="clock" id="clock">99:99:99</div>
     <div>
        <iframe id="cometFrame" name="cometFrame" width="0" height="0"
               border="0" style="border-width: 0px"/>
    </div>
</wicket:panel>
</body>
</html>

Here we use Atmosphere to send the request and display the asynchronous response from the server (the callback function). For demonstration purpose, we set the default transport to Websocket but this is optional. What the PlugIn will do here is first try to use Websocket support from the Browser. If Websocket is available, it will use it to make a request to the server. If the server reject the request because it doesn’t support Websocket, it will fallback to Comet. For our demo, it fallback to long-polling, but you could change it to streaming and it will still works. For this demo I use an iframe to display the clock update.

Wow. Without being a Wicket expert, I consider this demo quite easy to do. Let me know what you think! You can download the complete application from here (rename to atmosphere-wicket-clock.war). You can also checkout the code on Github.  The sample has been done in collaboration with Andrey Belyaev.

For any questions or to download Atmosphere Client and Server Framework, go to our main site and use our Nabble forum (no subscription needed), or follow the team or myself and tweet your questions there!

  1. ferjon
    October 13, 2010 at 1:23 pm

    Great. perfect resource to implement my Wicket-Atmosphere application.

    Thanks,

  2. martin-g
    November 9, 2010 at 8:55 pm

    Hi,

    I just tried the application and it seems the scheduler doesn’t make proper use of Callable. Instead of returning the date as String it returns Callable’s toString(), i.e. …PushPage$1@abcdef.
    While Wicket can help to make the setup quite easy and reusable I think that PushPage itself should be a normal Servlet. There is no need to use a Page for that use case.

    Good blog, congrats!

  3. martin-g
    November 10, 2010 at 12:39 pm

    Hi Jeanfrancois,

    Actually I tried with 0.6 and then with 0.7-SNAPSHOT which I build locally (cloned from Atmosphere’s GitHub). I’ll push my version my github account soon and let you know.
    For the record I used Jetty 7, but I guess exactly this Meteor behavior (the scheduler) doesn’t depend on the web container.

  4. Jfarcand
    November 10, 2010 at 12:43 pm

    Salut,

    Atmosphere 0.6 doesn’t support Callable so I suspect it may be the issue. I’ve just tested on Jetty 7.2 and it worked for me. Let me know when I can take a look.

    Thanks!!

    — Jeanfrancois

  5. martin-g
    November 12, 2010 at 10:32 am

    Salut,

    Here is the github project: https://github.com/martin-g/wicket-atmosphere-tests
    It uses Atmopshere-0.7-SNAPSHOT with Jetty 6.1.25. Maybe the Jetty version is the reason to see CometServlet$TaskCallable@fafaaf.
    I’ll update it to Jetty 7 anyway.

  6. martin-g
    November 12, 2010 at 10:56 am

    Ok, the application is upgraded to latest Jetty 7 and I still see the problem.
    Use either ‘mvn jetty:run’ or start org.atmosphere.samples.wicket.Start#main(String[]) and go to http://localhost:8080

    • November 12, 2010 at 8:27 pm

      Salut,

      the application works fine for me. There is something broken with your atmosphere jar file. I’ve forked your project and it work as expected🙂

      A+

      — Jeanfrancois

  7. martin-g
    November 13, 2010 at 8:13 pm

    Hi Jeanfrancois,

    You were right. I rebuilt it and now I had to add the new onThrowable() method.

    Now I have a new problem. In Google Chrome 9.x console I see:

    Invoking executeWebSocket jquery.atmosphere.js:532
    Wrong url scheme for WebSocket http://localhost:8080/comet/clockPanel1 :8080

    And the client side doesn’t do anything more.
    The servlet logs onBroadcast calls.

    With Firefox 3.6 everything works OK.

  8. November 15, 2010 at 12:54 pm

    Salut Martin,

    Google Chrome 9.x ? Which version exactly is that, because I’ve tested with 7.0.517.44.

    Thanks!

    — Jeanfrancois

  9. Rodolfo Hansen
    November 15, 2010 at 4:54 pm

    Hi, I am one of the developers for the wicketstuff-push project ?

    I am interested in getting it to work with Atmosphere and seeing if we can pool efforts in making push a common tool in the wicket arsenal!

    • November 15, 2010 at 5:15 pm

      Salut,

      this is great news. Ping me at jfarcand [at] apache [dot] org so we can see where we can collaborate. I’m really interested to help!

      Thanks!

      — Jeanfrancois

  10. Andrey Belyaev
    November 18, 2010 at 4:53 pm

    It looks like Google has indexed “wicket” and “push” words here🙂 I played with the Wicket Clock prototype for some time. Here are some concerns:
    1. IE and Opera does not support streaming well, so we have to fallback to long-polling.
    2. The prototype doesn’t work with wicket 1.5 at all.
    3. Running this prototype on Jetty 7 with websocket support throws NPE because wicket cannot get context info from websocket call.
    4. Running on Glassfish 3.1 causes Grizzly error
    5. When the clock ran for the whole night in Chrome(Streaming), Safari (Streaming), IE8 (long-polling), Opera 10.63(long-polling), Firefox 3.6(Streaming) and Tomcat 6 on server-side in the morning I found that all browsers are dead, but clock continue working and broadcasting.

    I’ll provide more info when I come back from devoxx

    • November 18, 2010 at 6:03 pm

      Salut,

      thanks for the feedback. Like I said in the blog, it’s really to demonstrate how to do it. I haven’t tested “production wise” the sample…. I’m surprised about IE and streaming (Opera I can reproduce). Can you share the NPE? GlassFish 3.1 is broken right now as they changed their API and I haven’t had a chance to work on it. But Jetty should work. I will test the 24 hours runs🙂

      Thanks!

      — Jeanfrancois

  11. Andrey Belyaev
    November 18, 2010 at 6:40 pm

    Hi Jean-Francois,

    I think I’ll send you all errors on Monday, 22 november.

    As for Jetty, it is not a Jetty problem, but Wicket’s, as far as I see. It looks like that request a wicket page via websocket was a not very good idea.

    Could you tell me how to add more verbosity to jQuery plugin log? I just want to provide also IE8 client-related information.

  12. Jfarcand
    November 19, 2010 at 12:50 pm

    Hi Andrey,

    I’ve “fixed” IE (there were a configuration issue). A special BroadcastFilter must be installed in order to support streaming. So I’ve just added:

    org.atmosphere.cpr.broadcastFilterClasses
    org.atmosphere.client.FormParamFilter,org.atmosphere.client.JavascriptClientFilter

    I’ve also fixed a glitch with Callable so make sure you download the latest 0.7-SNAPSHOT. I was aware of Opera (will fix it soon). I will take a look at Websocket but so far it works for me on Jetty.

    Thanks!

    — Jeanfrancois

  13. Jfarcand
    November 19, 2010 at 7:12 pm

    OK, Opera is back to work (seems Server Side Events API has changed, so I’ve disabled it for Opera for now). Download latest 0.7-SNAPSHOT.

    For debugging, you can increase the log using $.atmosphere.request.logLevel

    Let me know if you still see issue with IE.

    Thanks!

    — Jeanfrancois

  14. martin-g
    November 22, 2010 at 4:22 pm

    Hi Jeanfrancois, Andrey,

    At https://github.com/martin-g/wicket-atmosphere-tests I have fork you this demo upgraded to Wicket 1.5 and made a bit more Wicket-ish.
    I still cannot make with working with Chrome 9.x but it seems it is a problem with my local Atmosphere build.
    About the WebSocket error: can you zip your application and attach it in a ticket in Wicket’s Jira: http://issues.apache.org/jira/browse/WICKET.

    • Andrey Belyaev
      November 22, 2010 at 8:46 pm

      Cool, Martin, thanks!
      Will look at it tomorrow morning. Could you please write me to belyaev.andrey (at) gmail.com, I think it worth to include you in CC when I write to Jean-Francois (and vice versa).

      Or we can discuss it at maillist for more visibility. WDYT?

  15. Jfarcand
    November 22, 2010 at 8:55 pm

    I think the users@atmosphere.java.net should be a good place to continue the discussion.

    @Martin: I can reproduce the issue with ws. The url passed with $.atmosphere.subscribe(“../resource”), and the JQuery Plugin doesn’t handle that case properly. I will work on it.

    Thanks!!

  16. martin-g
    November 23, 2010 at 6:51 pm

    I just subscribed to users@ mailing list. Will try to help with any Wicket issue😉
    @Andrey: I’m still waiting the ticket in Wicket Jira😉

  17. Ondrej Burkert
    December 20, 2012 at 3:08 pm

    Hi, I started from your post and from Wicket-Atmosphere project description. I encountered few problems with Spring Security on the way so I finally decided to write it down so someone else can spend four days on other problems:)

    http://burkond.blogspot.de/2012/12/apache-wicket-spring-security.html

  18. March 29, 2013 at 1:31 pm

    Very nice post always bring with a set of useful comments! Thanks for all!

    If would be possible to extend this example but for not show only one clock server but a dozen of clock servers with different states or counters (not the same value). Which approach could be useful for this parallel scenario? Using a thread framework? or another asynchronous tool like vert.x? Or maybe scala actors if the implementation app turns to scala + wicket instead java + wicket?

  19. March 29, 2013 at 1:38 pm

    I’m working in a scenario that is necessary keep on server many clocks objects as many different session users [browser clients]. I need to show all the clock counters or state in an administrative panel for a special admin user. I’m looking for a god strategy to do this using wicket, mainly.

  1. January 11, 2012 at 9:50 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: