out.println

This is my blog. There are many like it, but this one is mine.
Subscribe Home Contact

java

Java collections class diagram

Here's a quick reference diagram of the JDK collection classes.

I wanted something concise (just showing what's available, for more details there's the javadoc), user-oriented (the abstract implementations are not represented), and that could be printed on an A4 or US-letter paper sheet.

preview

I've used yEd. If you want to tweak the diagram or export it to another format, the source is available on GitHub.


html5 offline webapp local storage play!

An offline webapp example with Play! and JQuery

I've published a small prototype written to experiment with HTML5's offline webapp and local storage features. It's a simplistic note-taking application, with two list and detail pages:

screenshots

Notes can be browsed, created, modified and deleted offline; they are stored locally and synchronized when the application goes back online. To keep the example reasonably short, I've simplified a few features:

  • conflict resolution is optimistic: when both a local and a server version of a note exist, the next synchronization will keep the most recent version;
  • removal is "soft": the note stays in the database, but gets flagged as "archived" so that future search requests ignore it.

On the technical side, the backend relies on Play! and JPA, and the frontend uses JQuery.

Here are a few thoughts gathered during development:

Writing the manifest

Making the list page cacheable

There is something counterintuitive about offline pages in a dynamic website. When I first learnt about the cache manifest and the fallback section, I thought my list page would use a "normal", server-generated version in online mode (query the database, feed the results to a Play! template that generates the HTML response), and would somehow fallback to a different, local-storage-enabled version in offline mode.

This is not how it works. The key point here is this sentence from the specification:

in practice the page that referenced the manifest is automatically cached even if it isn't explicitly mentioned.

Let's suppose that I navigate to / (the URI of my list page) while online; my server-side components generate the HTML response and send it back to my browser. Since this document declares a manifest, the browser stores it in its offline cache (along with any resource declared in the manifest), and uses this cached version for any subsequent request, be it online or offline. Which means that:

  • refreshing the page won't send a new request to the server. If the database changes, I will keep getting a stale version of the list;
  • when the application goes offline, I have no way to switch to my alternate version of the page. What gets served is the cached version of an earlier online request.

The bottom line is that there must be a single version of my list page, which works in both modes:

  • when online, it fetches data from the server using background Ajax requests;
  • when offline, it defaults to local storage.

This moves the solution towards a "single page"-style interface, where most of the application logic (fetching data, populating the list, pagination) is handled via client-side Javascript.

Keeping the detail page addressable

One thing we lose with client-side navigation is addressable URIs. I wanted to keep a RESTful-style URI for each note (/notes/7 in the second screenshot above).

I did that by keeping the detail page as a different resource. However, there is again a problem with the offline cache: each note URI is interpreted as a separate resource. Since IDs are assigned dynamically, I can't list each and every /notes/<id> URI in my cache manifest in advance. So, if I'm offline and try to access a note I've never opened before, the browser won't find it because it's not in the cache. I should be able to restore this page from the data I've synchronized in my local storage.

To address this problem, I've used the fallback section. All dynamic note URIs fallback to a static page:

NETWORK:
/notes/*

FALLBACK:
/notes/ /offline/detail

The fallback page uses Javascript to parse the URL and load the note (again, either with a background Ajax call or from the local storage, depending on network availability).

Testing

I've used Play's out-of-the box Selenium support.

The first question was: how to simulate loss of connectivity? Since Play's test runner is launched from the developement server, I couldn't just shut it down. The answer turned out to be quite simple: the way my client code detects offline mode is by the fact that Ajax requests fail. So I added a "hook" URI that makes all Ajax requests 404 (this is handled by setting a flag on the server). The Selenium tests just need to invoke that URI, and the application behaves as if it was offline.

Similarly, I've added other hook URIs to:

  • clear the local storage (by loading a page that runs a Javascript snippet);
  • touch the cache manifest, to force an offline cache update;
  • check the state of a note on the server without going through the normal UI (this is useful to check that the server is not updated when the client is supposed to be disconnected);
  • touch a server note (to test update conflicts).

Naturally, these URIs are only accessible when the server runs in development mode.

Conclusion

In my experience, taking a dynamic (as in database-driven) webapp offline naturally lead me to a single-page design. I've kept the list and detail page separate, but there is probably a way to keep everything in one page and still preserve addressability.

Things get complicated quickly. I've spent more time on this tiny example than I had originally anticipated. In particular, keeping the local and server databases synchronized introduces new corner cases.

Testing can be painful (now I understand why Dive into HTML5 has a section named "The fine art of debugging, A.K.A. Kill me! Kill me now!"). Locate your browser's "clear local data" and "clear cache" buttons, you're going to need them often.

Running the Selenium tests from the browser is probably not the best choice. Standalone tests (for instance, JUnit tests using Selenium's Java API) would have made it easier to simulate offline mode, simply by shutting down and restarting the development server.

With Firefox, I've sometimes observed erratic offline cache behavior: it would stop updating and I would have to clear it manually and restart the browser. It's not clear to me whether this is a Firefox bug or a problem with my configuration.

Feel free to comment or fork the GitHub project.


blog

Moving feed to FeedBurner

I've published my feed on FeedBurner.

If you had suscribed to the former URL (which will still work), I'd appreciate if you took a few seconds to change to http://feeds.feedburner.com/out-println. This will provide me with stats and take a little bit of load off my server.

More technical posts are coming :-)

Archives

  • 2011
  • 2010