Archive for the ‘GDD07’ Category
Why Blogger Needs Offline Editing Support, and How To Build It With Google Gears
I’m writing this in Notepad while riding the BART train on my normal morning commute into San Francisco. This commute takes about forty minutes each way for me. Since there’s no Internet, I can’t connect to work at all. Hence this is dedicated time for reading, or writing, or hacking on things that don’t need a full development environment (my laptop is a bit too old to cope with our entire code base from work anymore).
So of course blogging would be a natural. And sure, writing in Notepad is not torture. But it’s also not the right thing. What I want is to be able to write blog posts, edit them, save drafts, etc., etc., and then the minute I get back online, upload ’em all to Blogger and worry no more about it.
To do that requires some kind of offline application with local storage. As my last post mentioned, I originally wanted to build such an offline app as a standalone Tomcat-based Seam webapp that would run in a local webserver. But Google Gears offers a much more direct route to the same destination.
So this post is a breakdown of how to construct such a client, based on conversations with various Googlers last week at Google’s Mountain View developer day.
The Requirements
Must-have requirements for an offline Blogger editor:
- be a web application running in the browser (duh!)
- support rich text editing at least at the level of Blogger’s own online editor
- support authentication to Blogger
- support local storage of some or all of the user’s blog posts
- support creating, viewing, drafting, and editing of posts whether online or offline
- allow synchronizing any offline edits to Blogger once back online
- allow synchronizing of any posts edited on Blogger down to local storage
- allow very minimal reconciliation of conflicting edits to posts on Blogger
- require minimal installation of plugins or other application-specific software
Nice-to-have requirements for an offline Blogger editor:
- support archiving / backing up of a user’s entire blog
- support re-importing some set of content to a new blog that supports the Blogger API
- support migrating content between blog services
- support deleting postings ahd handling synchronization of offline deletes
- provide a nicer browsing/reading UI (arguably Google Reader has this job sewn up already)
(Needless to say, the nice-to-haves don’t get done until all the must-haves are working!)
Explicit NON-requirements:
- No need for this application to render any static HTML (the blog can presumably still be viewed and searched through Blogger itself; this app can be *just* an editor)
The API Implementation
Clearly this is exactly the kind of application Google Gears was created to support. So let’s assume we’re using Gears. Further, since I like GWT a whole lot, let’s also assume we’re using GWT.
I originally thought that this app could be built as a GWT/Gears webapp connecting directly to the Blogger API. Dan Morrill of the GWT team set me straight on that. The Blogger API is very pure in its REST-fulness, meaning specifically that it uses all the HTTP verbs. However, XMLHTTPRequest, the entire foundation of AJAX, can’t do anything other than GET and POST. So whatever we do is going to have to involve some kind of servlet bridge to the Blogger API.
(Possible workarounds for this: 1) get the Blogger and GData API teams to make GET/POST-only variants of their PUT/DELETE API methods; 2) get the browser vendors to implement PUT/DELETE support in XMLHTTPRequest. Clearly #1 is a lot more doable than #2 given the dreaded words, “legacy support”. You listening, GData team???)
Once we’ve accepted the need for some kind of bridge servlet, we’d better make sure that bridge servlet is fairly scalable and generic. Whoever is hosting this bridge servlet is going to have potentially a lot of traffic. So the bridge servlet needs to be stateless, re-entrant, and as close to a simple pass-through as possible. It shouldn’t have per-session state; it should be just a message relay.
We’ll also need some GWT modules for communicating with the bridge servlet, including beans that map to the data objects defined in the Blogger API, and service interfaces that map to the Blogger API’s methods. And in fact, this suggests that the servlet itself should simply use the Java implementation of the Blogger client API, exporting a GWT-friendly version of it (via GWT RPC) to the GWT client application.
The persistence implementation
Gears has this lovely local SQL database. But how do we get things in and out of it? We could write to the JDBC-like Gears SQL API. But I have tasted Hibernate. And if I’m programming in GWT, then writing JDBC-like code by hand would be like going back to the pre-Hibernate days. And I can’t do it. I CAN’T DO IT, I TELL YOU! I can’t go back. I WON’T go back!
Now when I first started brainstorming this with Ray from timepedia.org at GDD07, we got wild-eyed and started contemplating porting Hibernate to GWT. Saner heads rapidly prevailed. It turns out that Bob Vawter of the GWT team is doing work on a GWT-generator framework for generating a variety of useful artifacts (starting with Javascript-library method exports) from GWT beans.
So let’s use his framework, only instead of exporting to Javascript libs, we’ll make it generate dead simple CRUD operations for our Blogger beans to the Gears data store.
It looks like some other folks on the Gears list have had similar thoughts. Check out this Gears ORM implementation. But since that’s not based on GWT, it’s not directly applicable. Still cool though! But let’s assume we’ll proceed with Bob’s stuff, just for fun. This is all still early days and all experiments still deserve love.
The todo list
So, what do we have?
- GWT beans mapping to Blogger API data objects.
- GWT service interface mapping to Blogger API methods.
- Bridge servlet implementing GWT service interface and using Blogger Java client API implementation to relay messages to Blogger.
- Simple persistence code generator (using a GWT compile-time generator implementation) to take the GWT beans and store them in the Gears local database.
- Business logic to implement the client-side synchronization to the Blogger server. (That’ll be another post, or maybe lots of them!)
- GWT UI code to let the user use all of this infrastructural goodness to meet the must-have requirements!
If we get it right, it hopefully lands in the Google GWT API project. And once Blogger does this, Calendar is next, and then some.
Sounds like a fun project, huh? Interested? Chime in in the comments. Or join this GWT-Contributors thread on the topic.
Onwards!
Why start this blog?
[ObNewBlogIntentions] Will try to update often. Will try not to spend so much time farting around with templates that I never get around to posting. [/ObNewBlogIntentions]
So, with that out of the way, hello. I’ve been planning a blog for a year and a half now. I realized in early 2006 that I’d spent my whole life programming for companies who owned everything I did, and I wanted to do something open source on my own time. So I thought, what about a blog system? Clearly the world needs more of those 🙂
(The nice thing about programming on your own time is you don’t have to justify anything to anybody! Except your wife. But if you cut back on the computer games, so you’re not blowing your personal time budget away, then it’s all good.)
The thing is, I really wanted a blogging system that would support offline editing and offline backup of my blog content. I ride the commuter train, and I don’t entirely trust any single system (even one run by Google) — I like lots of redundant backups that I personally maintain.
So I started on a project to implement a distributed blog application. Which, of course, since I’m a Java weenie, meant that I really needed a Hibernate-based peer-to-peer distributed object version control system.
I built most of that over the bulk of 2006 (or prototyped it, anyway), using a Seam / EJB3 / /Tomcat / JSF stack. Then I thought, how do I build the UI? I started looking into GWT and liked it. I even found a JSF / GWT integration library. But it didn’t handle GWT RPC properly.
Then GWT got open sourced. I started working on fixing the RPC issues in GWT in early 2007, and that led to my submitting what I think was the first major external patch to GWT. I also put together a demo of Seam + JSF + GWT — adding a GWT blog-reading interface to the Seam blog example.
Michael Yuan from JBoss mentioned this at his JavaOne BOF this year, and I got to demo it at the GWT hack session at JavaOne, where I had the pleasure of meeting Bruce, Joel, Kelly, Bob, and the Dans (Peterson and Morrill) from the GWT team. Then I took a couple of weeks off to recuperate — my wife is expecting our second child in late July (our first child is two and a half), so there’s plenty of other distractions keeping me busy!
So that brings us to yesterday. Google Developer Day in Mountain View. And the announcement of Google Gears and GWT for Gears. Now, remember that my original use case was to build a blogging app that could work offline. I was using a technology stack that was Seam + EJB3 + Tomcat + JSF + GWT + Postgres. So to use this app I was building, you’d have to be running a local Postgres install and a local Tomcat install, and you’d have to do a bunch of Java deployment. I might have wound up with a few dozen users, or maybe only myself. (The code could have wider applicability, but direct product usage would’ve been low.)
Along comes Gears. Suddenly there’s a whole new stack option: Gears + GWT + Blogger. Offline functionality usable with any Blogger blog, and a much, much, much easier deployment, with a possibly much larger user base as a result.
So yesterday at GDD07 I brainstormed about this a bunch with Dan Morrill, Miguel Mendez, and Bob Vawter from the GWT team. It looks like a go. My next post will be about the detailed plan for that.
Meanwhile I still have to finish landing the JSF+GWT code, and documenting and submitting the Seam example. This whole project has been one surprise after another, and it’s only getting more fun so far 🙂
Onwards!