This week Google announced Java support for AppEngine. I had signed up when they originally announced Python support, hoping to use it as an excuse to learn Python, but never did anything with it, mostly because I’ve been spending so much time with Grails. So when they announced Java support I immediately signed up and was approved in about an hour.
Guillaume Laforge blogged about Groovy support – they’d worked with Google to ensure compatibility and released v1.6.1 with the required fixes. Of course with Groovy support, lots of people were asking if Grails would work. Unfortunately it looks like it’ll take some work for that. So far it looks like there are problems with threads, XPathFactory, and SiteMesh, and of course GORM will have to be enhanced to work with the AppEngine datastore.
Lots of work is being done to get frameworks working in AppEngine. Shay Banon updated Compass and posted a screencast demonstrating its usage. Thomas Mueller updated H2 database, so in-memory JDBC is possible, and he’s planning on writing a JDBC driver wrapping the datastore API which would make porting apps a lot more straightforward. In addition work is being done to make Tapestry, Struts, JSF, and other frameworks compatible.
The SDK supports both JPA and JDO, but JDO is currently much better documented. I have no experience with JDO so I started playing with the “jdoexamples” demo project that ships with the SDK. Like most of the examples it uses Servlets and JSPs, which after using SpringMVC and Grails seemed painfully dated. And the database code is very repetitive with lots of boilerplate. So I thought I’d play with using Groovy to add Grails-like functionality to reduce some of the clutter. This also gave me an excuse to use the new MVC features in Spring 2.5, especially the annotation support.
I reworked the jdoexamples project as a Spring MVC application, along with a bunch of Groovy MetaClass magic. There’s no BeanBuilder or resources.groovy/resources.xml; instead the “core” applicationContext.xml includes two other context files, one that’s auto-generated during the Ant “war” target containing services, and one that is application-specific in
The domain classes aren’t changed much except that they take advantage of being written in Groovy, and static inner classes were reworked as top-level classes since Groovy doesn’t support inner classes. The “Utils” classes were reworked as Services. The Servlets were reworked as Controllers, and all of the scriptlets in the JSPs were moved to the controllers or reworked using JSTL tags. I didn’t improve the UI (it’s pretty sparse) since I was more focused on the code and not the look and feel.
The code expects a directory structure similar to a Grails project: domain classes in
grails-app/domain, services in
grails-app/services, controllers in
grails-app/controllers, Java source in
src/java, and Groovy helper classes in
In addition there’s the framework classes (in
src/framework, there’s no separate library for these) and core
applicationContext.xml, some application-specific config files in
src/java (which will end up in
WEB-INF/classes), application jars in
lib, web content in
war, web config files in
war/WEB-INF, JSPs in
war/WEB-INF/jsp, and Groovlets in
war/WEB-INF/groovy (these aren’t auto-discovered, they need to be manually registered in
web.xml as Servlets).
During the build, a file is generated (
WEB-INF/artifacts.config) containing the names of the domain, controller, and service classes; this way the framework code knows which MOP methods to add when the app starts up.
The added domain class MetaClass methods are:
withPersistenceManager (to execute code with access to the PersistenceManager)
jdoMakeDirty corresponding to the JDOHelper methods
Controllers have these attributes added to their MetaClass:
params (with Request attributes like in Grails)
These attributes provide controllers access to the standard servlet classes but they aren’t as useful as in Grails since the approach taken is to use the newer MVC features in Spring 2.5 like declaring typed (auto-converted) parameters using annotations in controller methods. But they’re available in case they’re needed.
All artifacts have a Commons Logging “log” (wrapping a java.util.Logger) wired up, although for some reason this doesn’t work in Services. The only thing I can think of is that it’s because they’re Spring beans, but that shouldn’t matter since they’re not proxied.
Controllers are singletons. I appreciate the convenience of having stateful per-instance (prototype scope) Controllers in Grails but I tend not to take advantage of that and prefer the more traditional stateless singleton scope approach.
There are no Spring samples in the downloadable SDK demos, but after I’d written most of the demo app code I saw on the mailing list that the autoshoppe demo had been checked into the trunk (along with a few others). I should have expected that Spring would have a JdoTemplate – I’ll need to rework some of the code to use that since I reinvented a few wheels.
There’s a lot of missing functionality. No taglibs, scripts, converters, or GSPs. Also no SiteMesh, no BeanBuilder, and obviously no plugins But it mostly works and definitely makes Spring/AppEngine development easier.
In addition I was thinking that I’d integrate Compass, plus it shouldn’t be much work to add Spring Security – it should only need a JDO-based UserDetailsService implementation.
You can download the sample code here and view it in action here.
Note that to use Ant to build the app, you need to copy groovy-all-1.6.1.jar to $ANT_HOME/lib. You also need a build.properties with an “appengine.sdk” property pointing to the root directory of your AppEngine SDK directory (to load the shared Ant targets). And you’ll need to change the app name (“bb-grails-lite”) in
appengine-web.xml. You’ll also need to change the Controller package name in
com.burtbeckwith.gae.guestbook.controller to whatever your Controller package is.
There are two issues with the online demo. One is that the “friends” demo displays an error page – there are error messages in the console logs complaining about a missing index (this works fine locally). The other is that the “addressbook” demo mostly works, but it doesn’t save the
phoneNumber fields correctly. This means that you can’t search by state, only by last name, and it displays the
state value instead of the
phoneNumber value. As you can see from the screenshot of the admin console, it thinks that there’s a
metaClass field instead of
state, and isn’t aware of
phoneNumber at all. This seems highly likely to be due to Groovy, but it’ll have to be investigated. If the domain classes can’t be written in Groovy, there’s a lot of lost functionality without being able to use MOP.