10 things I love about Apache Wicket

…and wish I had known right from the start!

Wicket is awesome. But…

Even if you instantly like Wicket’s Object-oriented modular approach, you have quite some learning ahead of you before you can use it’s powers well.

There are plenty of good books, example website and tutorials out there, and the code is well-written. So there’s nothing to worry about. Eventually, you will grasp it.

But if you’re like me, you read fast, and thus miss some points. I learnt a lot by reading answers by smart people in discussion forums. Here’s me giving back something to the community: My “must-know list” I wish I had had when I started learning.

Nothing new or exciting for seasoned Wicket developers. But maybe something for newbies. And definitely a must-read for anyone starting in our new company.

Wicket Session size!

This is something I learned about way too late! You have to use Models for just about everything, or your session size will simply explode. This may not be a problem for a rarely used application on a powerful server, but it can become a nightmare if you have thousands of users in parallel, or if you are using Google App Engine like us, which enforces a strict 1MB session size maximum.

We did a separate document for this topic alone. Read more about optimising wicket session size here.

Enclosures

I rarely read documentation. So it took me several months of dead-ugly code to figure out some of the most basic cool feature of Wicket. It’s the enclosure.

Consider this problem: You want to display an optional value. To display it, you need additional context. What do do with the context when the optional value should get hidden?

Maybe you have a user object, and each user has a first name, and a second name. And then some users have a nickname too. You display them in a table, each one gets a row.

Last name Smith
First name John
Nickname Johnny

Now let’s assume some people don’t have a nickname. That looks ugly be default:

Last name Smith
First name John
Nickname

So let’s assume you don’t want to show the Nickname row for people who have no Nickname. You could either introduce a WebMarkupPanel for the entire row, add the nickname label to it, and if the nickname is null, make that WebMarkupContainer invisible.

Or you could take the shortcut: Set the visibility of the nickname-Label to invisible, and let an Enclosure handle the rest:

<table>
    <tr><td>Last name</td><td><span wicket:id="lastName"/></td></tr>
    <tr><td>First name</td><td><span wicket:id="firstName"/></td></tr>
    <wicket:enclosure child="nickName">
        <tr><td>Nickname name</td><td><span wicket:id="nickName"/></td></tr>
    </wicket:enclosure>
</table>

Sweet!Obviously this is a very simple use case. Enclosures are super useful especially in complex scenarios, and we use them day in day out. It just took us a while to figure out they existed.

Session fixation prevention vs login intercept redirection

It seems to be good practice to call session.replaceSession() to prevent session fixation attacks. That's fine, and it works on my local machine. However, if run on Google App Engine (and presumably other clustered web servers, since GAE just uses Jetty) then there's a problem, which took me months to realise.

When people want to get to a certain password-protected page, and they have to log in, then they get redirected to our login page. Once logged in, they continue to their desired page. Or do they? Well, not anymore after we added the replaceSession().

The replaceSession kills the redirection, but only on the actual webserver. What we ended up doing was gather the continuationURL, store it, replace the session, and then force th continuationURL onto Wicket again. I bet there are more elegant ways, but Googling around didn't find me a better solution yet, so here is the code to get and set the destination:

    private String getOriginalDestinationUrl(Page page) {

        PageMap p = (PageMap) page.getPageMap();

        Field field;
        try {
            field = PageMap.class.getDeclaredField("interceptContinuationURL");
            field.setAccessible(true);
            String inter = (String) field.get(p);
            log.info("got " + inter + " from field access");
            return inter;
        } catch (Exception e) {
            throw new RuntimeException("error getting the continuation.", e);
        }
    }

    private void setOriginalDestinationUrl(String value, Page page) {
        PageMap p = (PageMap) page.getPageMap();
        log.info("Setting intercept to " + value);
        Field field;
        try {
            field = PageMap.class.getDeclaredField("interceptContinuationURL");
            field.setAccessible(true);
            field.set(p, value);
        } catch (Exception e) {
            throw new RuntimeException("error setting the continuation", e);
        }
    }

Thanks to Nico and Stephan from tasqade for this hint!

Attribute Appenders. Pure Win. Or Pure Evil? It depends.

You have to love AttributeAppenders. A simple, foolproof way to dynamically add a CSS class to your Label, or to add a title to your Image. Dynamically, that is. How else would you get those nifty tooltips when hovering on an image, or a different CSS class depending on whether we're currently dealing with positive votes (green colour!) or negative votes (red color!). You can even pass your Components around through your app, add multiple Appenders, and Wicket ensures there's no duplication in the resulting code. I used AttributeAppenders a lot because they are so fun!

    userLogo.add(new AttributeAppender("title", new Model(userName), ""));
    userLogo.add(new AttributeAppender("class", new Model("userIcon"), ""));

Until I realised how heavy they can be on the server. App Engine doesn't like big sessions, and neither do I. There's a hard cap of 1MB when App Engine refuses to store your session, and even in the 200K region I get uneasy, since even these 200K incur serialisation and deserialisation costs. My main gripe with Wicket is that it stores all its components in the session for the last n pages you visited. This does add up. Now, the problem is that AttributeAppenders also end up in the page tree and thus in the session. And if you were as happy-go-lucky as me, then chances are you have many AttributeAppenders in there. Each one may just cost 40 to 120 bytes or so. But if you display 30 rows of data, and each row is heavily decorated and linked (author! recipient! project! parent message! votes! etc) these hundreds of Appenders do add up too!

Fortunately, I was able to cut the number in half by analysing each Appender and checking if there's some other way. Often I had just added Appenders because I was lazy. Or because I thought some tooltips might add value (but they didn't really). Sometimes I had used Appenders because I thought it was a nice way to factor out the classes into ONE spot, even if the links were built from 20 different pages. Sometimes I had one class for case A, and one for case B (e.g. red negative votes vs green positive votes), and I could make one the default, and have a class only for the less likely case (negative votes in my example).

So there's no need to panic or stop using AttributeAppenders, and premature optimisation is evil anyway. Use AttributeAppenders for 99% of the cases. Only if you just know that a certain link will be pervasive through the whole app, it may make sense to think about memory and performance.

So in the above example, I keep using the title to display the username since there's no other way. But the class now is wired into the component in the HTML.

Attribute Modifiers

There are times when you need to attach CSS class X under these conditions, and class Y else. Like adding some visual cue that a component in a list is active (green) or inactive (red). It's easily done with AttributeAppenders, as discussed before. But if you also want to use Wicket AJAX to toggle the state of the component, then the class needs to update. This doesn't work with Appenders, you need dynamic Attribute Modifiers! Their downside is that they overwrite any existing attributes (e.g existing CSS classes) so I typically start out with Appenders, and only use Modifiers when required)

Here's an example of putting a class onto a listItem (in a user list) depending on whether a user is active, locked or never logged in before.

listItem.add(new AttributeModifier("class", new Model() {
    @Override
    public Serializable getObject() {

        String cssClass="userRow ";
        User user = userModel.getObject();
        if ( !user.isActive() ) {
            cssClass+="userLocked ";
        }

        if (user.isGuest()) {
            cssClass+="userGuest ";
        }
        if (user.getLoginCounter()==0 && !user.isFake()) {
            cssClass+="userNeverLoggedIn ";
        }

        return cssClass;
    }
}
));

Pretty URLS, combined with lazy class loading

Surprisingly, some people don't know about the mount()-methods. These methods turn the default ugly URLs into something nice. It works like this

mountBookmarkablePage("/authenticate", PleaseAuthenticatePage.class);

However, if you have many URLs to mount (we have hundreds), then Wicket needs to do a lot of classloading at the startup. In some circumstances this can be a problem. Like, when you're on App Engine and instance startup has to be fast, and classloading can at times be slow. A lazy loading mounting can be very useful in that case. Using the class below, the mounting needs to be slightly adjusted, et voila you've got a blazing fast startup experience again.

private void myMount(String path, String className) {
    mount(new LazyBookmarkablePageRequestTargetUrlCodingStrategy(path, className,null));
}

...

myMount("/10-things-about-apache-wicket-i-love", "com.praisemanager.web.site.ThingsAboutWicketPage.class");

Want to toggle visibility of a panel with ajax?

setVisible(false) is great to hide a component based on some on-the-fly calculation, for example during the initial page rendering But to show that component later on during a Wicket Ajax request, you'll have to mark that component. Otherwise setVisible(true) simply won't to anything. Call

mycomponent.setOutputMarkupPlaceholderTag(true)

, in addition to setOutputMarkupId(true). Of course, don't forget to add that component to the AjaxRequestTarget.Admittedly, this is very basic knowledge. But it's a bug I kept running into a few times until I remembered.

Important addition: Override onConfigure()!It's pretty obvious that there is a setVisible() method on each component. It's not quite as visible that you shouldn't use this directly while you're processing an AJAX request. Instead, override the onConfigure() method, and inside it use setVisible(), based on the objects' models' state. The AJAX request merely states that the said component is dirty and needs to be re-rendered. ("addComponent()")

Careful with those AJAX requests

The Wicket AAJX integration is just great. It saves us just so much time. Of course we could use jQuery (and for more advanced stuff we do) but all the boilerplate code like hiding additional tabs, changing things on the fly, etc are taken care of. BUT:

Sessions time out, pages get evicted from the pagestore. This disconnects entry forms and Wicket AJAX actions from their back-end, and renders these ugly "Session expired" pages when people need them the least, e.g. after submitting a carefully crafted performance self-assessment. To some extent you just have to live with that. But to some extent you will have to mitigate the effects. Here two of the things we do to reduce the pain:

  • Whenever the browser window is reactivated after more than 5 minutes of absence, a jQuery snippet calls the server and asks a little JSON backend "is there a session for this user"? If not, then jQuery will render an overlay, letting the end user know that their session has expired, and that most likely their next action will fail. We do give people the option to ignore the warning and to continue working on the page, but at least they have been warned: overlay for wicket
  • Wicket AJAX is super useful and easy to build, so in most cases we rely on it. The same applies to regular forms. But there are pages where our users are simply known to stay longer. They will often leave the browser window open over night or over the weekend, and want to resume editing a form on another day. On these pages we do not use Wicket forms. The save-button (and the auto-save) on that form merely trigger JSON requests to store the data. This means that even if the session had expired, the save operation would still work. Oh sure, you need to take care of security matters all by yourself then, but we found that this extra effort was totally worth it. Before we had lots of complaints of users who lost their data due to Wickets page expiry, now it's almost none. Also, we autosave eagerly, so that even in unlikely events of failure the user gets a warning early on, and doesn't lose too much.

Include your resource files and have them cached in the browser. But only until the next deployment.

It is seriously easy to make panels, components and pages incorporate JavaScript and CSS files. Just put this into a panel, for example, and you don't have to worry about imports in the HTML file anymore.

    add(CSSPackageResource.getHeaderContribution(IndexPage.class, "global.css"));
    add(JavascriptPackageResource.getHeaderContribution(IndexPage.class, "jquery.tools.min.js"));

Now, about caching. On one hand you want stuff to be cached for a long time to make the UI more responsive. Ensure that your application server has a long timeout (or use getResourceSettings().setDefaultCacheDuration(); with some high value)

On the other hand, you want to clear caches after a redeployment. It's easy. Just append a timestamp to your resources (at least the ones that change frequently). How to get that timestamp? Easy too!

/** Method that loads a stuff from a file into a list **/    
public List loadFileToArray(String filename) {
    List loaded= new ArrayList();
    try {
        InputStream istream = this.getClass().getClassLoader().getResourceAsStream(filename);

        // Get the object of DataInputStream
        DataInputStream in = new DataInputStream(istream);
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String strLine;

        //Read File Line By Line
        while ((strLine = br.readLine()) != null) {
            loaded.add(strLine);
        }
        //Close the input stream
        in.close();
    } catch (Exception e) {//Catch exception if any
        System.err.println("Error: " + e.getMessage());
    }

    return loaded;
}

...

private static String timestamp=null;

/** base class constructor **/
public BasePage() {
    if (timestamp==null) {

        List l=new Util().loadFileToArray("timestamp.txt");
        timestamp=(String)l.get(0);
    }

    add(CSSPackageResource.getHeaderContribution("/css/global.css?v="+timestamp));

}

As you can see, we load a timestamp from a file called "timestamp.txt", and store it in a static variable. So it only happens once, upon rendering the first page. Then we add that timestamp as a parameter to our CSS file. Each time the timestamp changes, the browser will get a new version of it. The key is to generate the timestamp into the file when you rebuild your code. Easy. Use ant:

<target name="timestamp" description="creates timestamp for our resources">
    <delete file="${basedir}/src/main/java/timestamp.txt"/>
    <echo file="${basedir}/src/main/java/timestamp.txt">${time}</echo>
</target>

Don't bother componentising too early.

Wicket is perfect at modularising your code. I just love it. However, it is so simple that you really don't have to do it as the first thing. It is perfectly fine to delay it a bit, until your architecture becomes more stable, and until you know how your components are really going to look and interact.

A typical pattern when dealing with lists of objects was to have a page class (PerformanceReviewListPage), which contained a specific panel to handle the actual list (PerformanceReviewListPanel). Turns out that it's so easy to extract the list panel later on, you may as well do it later on. If you really need it, that is. In my case, I didn't really often end up reusing the panel, and most of our pages are not that terribly long either. But it blows up the number of files when using autocomplete in your IDE, so why componentise too early.

Want to load additional components into your page dynamically?

Say you are rendering a list of item titles, and for each item you want to be able to load a detail view right into the list. Or you have some tree-like structure, showing a list of items, and want to ajax-load sublists into an element.

There is an almost trivial way to do this. It is called replaceWith(). All you need is to add an empty placeholder component underneath each item in your list. Now, if someone clicks that ajax-link, you create a new panel with the same ID, and call placeholderPanel.replaceWith(newPanel). THAT'S IT! I love Wicket!

The main problem is finding this part of the documentation, or to realise this is what you need. I searched for plenty of keywords, didn't find anyhting, and ended up using JQuery's AJAX functionality to load a specific sub-list-page into the current one. Which is a major PITA with Wicket, because then you suddenly run into problems with Wicket pagemaps, and the dreaded Page Expired errors.

So, for all the poor souls looking for this solution, here are the keywords I used to search for:

  • wicket dynamic pages
  • wicket ajax dynamic
  • wicket modify page structure
  • wicket progressive enhancement

I hope this helped. Again, replaceWith() is what you're looking for!

Want to display a list of items that have totally different presentations?

Say, an event list. Event A has three properties, Event B has only 2, and Event C should always be displayed with some additional icons and colours. You could have one panel for each event type, but that's a bit of an overkill in most cases. On the other end, having a single panel render different types of events would result in nasty IF statements and overly complex code. Fragments to the rescue. (I found them a bit hard to understand at first, but it is really worth the effort. You'll have one ling but simple IF statement like this

    ...
    else if (type==Event.COMMENT_CHANGE_PROJECT) {
        listItem.add(new FragmentCommentChangeProject("eventDetails",
                            "fragmentCommentChangeProject", eventCreator,comment, project));
    }
    else if (type==Event.USER_LOG_IN) {
        listItem.add(new FragmentUserLogIn("eventDetails","fragmentUserLogIn", eventCreator));
    }
    ...

And then inner classes for each fragment, and neat simple HTML fragments like this

    <wicket:fragment wicket:id="fragmentUserLogIn">
        <a wicket:id="executingUserName"> <span wicket:id="userName">Andrew Lynch</span></a>
            logged in.
    </wicket:fragment>

Autocomplete field: How to deal with duplicates, how to deal with custom designs

There is a very simple autocomplete component in Wicket Extensions. You've seen it, it works fine for basic use cases. But it fails miserably for duplicates, and if you want to display additional text in the dropdown.

But let's assume you write a user-picker. Since the component is String-based only, it cannot tell duplicate names from each other. You have two "Joe Smith" in your list (and you can tell them apart by their avatar), and select the second one? Whoops, the component only knows you selected a certain Joe Smith. Which could be either the first, or the second. Duplicates just don't work.

So we went and wrote our own component.

Now it turns out there's an even better component out there. You will want to check that one out too!

 

We're hiring!

If you read this far, and if you're a student or a junior developer looking for a job on a really neat and modern application, we should talk! We use a whole stack of cool technology, there's no hierarchy or beauraucracy, and we're a nice bunch too. We're based in Berlin (with a satellite office in Sydney), and most of our customers are in the US or in Australia: Atlassian, Quiksilver, Opera, Klout, Modcloth, SugarInc just to mention a few.

We'd prefer you to be located in Berlin, but to some extent we're flexible. As long as you're able to make it to Berlin frequently (e.g. for a week every month) we should be able to work it out. We have high expectations, but do check out our Java and JQuery Developer Jobs in Berlin recruiting page.

We frequently offer a referral bonus, so if you know someone else, do check out the careers page as well.

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