Wicket 6 + CDI on Heroku

herokuwicketAs an Apache Wicket user for more than five years I really enjoy its programming model. I recently played with Play Framework 2.1 and Scala and discovered that deploying to Heroku is as easy as a git push. I wondered how difficult it could be using this mechanism to deploy a Wicket application.

Existing attempts

My initial investigation led me to this blog post from Martijn Dashorst explaining how to deploy a Wicket 1.5 application to Heroku, the service has slightly evolved since and the quickstart no longer deploys (Maven repository is no longer available to the run environment).

Adding JPA in the mix

I adapted it to deploy and use Wicket 6 instead of 1.5. And as I am a CDI fan I completed the quickstart with the CDI Wicket module (inspired from this post from Igor Vaynberg) and made the necessary steps to use the heroku bundled PostgreSQL database as a JPA datasource.

The key to make the database works correctly on Heroku resides in the following code snippet, where we parse the provided environment variable to populate hibernate properties.

try {
     URI dbUri = new URI(System.getenv("DATABASE_URL"));
     String username = dbUri.getUserInfo().split(":")[0];
     String password = dbUri.getUserInfo().split(":")[1];
     String dbUrl = "jdbc:postgresql://" + dbUri.getHost() 
                    + ':' + dbUri.getPort() + dbUri.getPath();
     System.setProperty("hibernate.connection.url", dbUrl);
     System.setProperty("hibernate.connection.user", username);
     System.setProperty("hibernate.connection.password", password);
} catch (Exception e) {
     LOGGER.error("Unable to extract database url");
}

Session replication

On Wicket mailing lists, a user recently asked the steps required to get the data store works correctly on Heroku (as the disk space is ephemeral), the key is using a NoSQL backend like Redis. So I wrote a simple and basic implementation of IDataStore using Redis (I think it can be optimized by someone familiar with Redis, pull requests are welcome).

Hands on

The quickstart can be found at the following address and deploys fine on a stack with Redis Cloud and PostgreSQL add ons enabled.
In the end you got :

  • Wicket 6
  • CDI via Weld
  • JPA with PostgreSQL
  • Redis datastore

You can see it live at the following address : http://wicket-6-sample.herokuapp.com/, the app can take a few seconds to start, as Heroku will stop it if it is idling for too long.

  • Carlson Weber

    Bon jour, monsieur Cedric, how are you?
    I tried your example today, as I am very anxious to get a wicket project scaling horizontally on heroku and I found some expected problems. I checked in your code on heroku and allocated 4 dynos for the app. For every 4 requests, at least 1 will result in page expired errors. I didn’t try so much to identify the source, because maybe you already saw something like this happening… So, do you have any ideas about what is going on?

    Thanks!

    • http://www.bloggure.info/ Cedric Gatay

      Hi Carlson,

      I guess there is a problem with my implementation of session replication using Redis. Unfortunately, I didn’t tried to spin multiple dynos on Heroku to assess that scaling is done right. However, I’ll try to ramp up multiple instances of my application on my local machine and see if I can get through the same kind of issue. Please let me know if you find the point that causes this bug.

      Regards

    • http://www.bloggure.info/ Cedric Gatay

      After digging a bit, I think it should work as long as Heroku correctly propagates the JSESSIONID cookie value to Wicket. Could you enable maximum logging in the `RedisDataStore` class and provide them? Does the problem occur on high load (not enough time to commit the session to Redis from one node) ?

      • http://www.bloggure.info/ Cedric Gatay

        Nevermind, I think I found the problem, I’ll update this post when an updated version is ready.

        • Carlson Weber

          Sorry I didn’t read your messages before! If you want, I can make any tests on this weekend, I am really up to have it working !! Meanwhile, thanks for your attention!

          • http://www.bloggure.info/ Cedric Gatay

            I think I did it wrong, I need to do an implementation of ISessionStore using Redis. Actually, if you hit a server without a session created, it will create a new one (discarding the data for the previous session).