Monday, February 22, 2010

Running Tapestry 5.2 Snapshot on Google App Engine

Google App Engine represents a great hosting option these days; it is free up to a fairly generous amount of server resources, after which billing kicks in at reasonable rate up to a budget you specify. Between that and the power and scalability of Google's infrastructure, it is seems like a great option for new projects (or existing projects if you happen to be using the right mix of technologies).

The only catch is that you're limited to a pretty specific set of technologies, and this does NOT include a relational database, or Hibernate! Hence, great for new projects, not so great if you have a significant app that relies on Hibernate or even a relational database paradigm.

My web framework of choice, Tapestry, is sortof supported, but it takes a little work to get it running on the local development server provided by Google.

As of this post, you'll want to be running on the Tapestry 5.2 snapshot. Tapestry 5.1 uses a stax parser that is incompatible with GAE. I'm currently running this on GAE 1.3.1.

Dmitry Gusev has a great post on how to fix a strange Javassist-related error "java.lang.ClassFormatError: Invalid length ... in LocalVariableTable". Unfortunately for me, it took a little extra work to get that fix incorporated into the development server. This thread from the Tapestry user list got me the rest of the way. You'll want to check out this post for some additional gotchas.

These instructions essentially explain the Tapestry list thread in a little more detail since I struggled a little to get it working. I am assuming you already have a working app running on Tapestry 5.2 snapshot, that you have an output "war" folder in the proper format for GAE to run against, and that you have GAE installed, with the /bin folder in your system path.

I'm using IntelliJ CE on Windows, so I am forced to use the command line or an ant script to run the server. These instructions show how to setup both.

1. Create and compile a class com.google.appengine.tools.development.agent.AppEngineDevAgent with the following source:

package com.google.appengine.tools.development.agent;

import java.lang.instrument.Instrumentation;

public class AppEngineDevAgent
{
public static Object getAgent()
{
return null;
}

public static void premain(String foo, Instrumentation bar) {}
}

2. In <sdk root>/lib/agent, extract appengine-agent.jar

3. Go into the extracted files to com/google/appengine/tools/development/agent and copy in the .class file created in step 2, overwriting the existing file

4. Re-jar the extracted files and call it patched-appengine.jar, putting it in <sdk root>/lib/agent

5. Backup <sdk root>/bin/dev_appserver.cmd and replace the contents of the original with this (replacing <sdk root>):

@java -javaagent:<sdk root>/lib/agent/patched-appengine.jar -cp "%~dp0\..\lib\appengine-tools-api.jar" ^
com.google.appengine.tools.development.DevAppServerMain %*

Alternatively, if you want to run the ant script provided by GAE, backup <sdk root>/config/user/ant-macros.xml and find the dev_appserver macrodef. Replace the <sequential> section with this (replacing <sdk root>):

<sequential>
<java classname="com.google.appengine.tools.development.DevAppServerMain"
classpath="${appengine.tools.classpath}"
fork="true" failonerror="true">
<jvmarg value="-javaagent:<sdk root>/lib/agent/patchedappengine.jar" />
<arg value="@{war}"/>
</java>
</sequential>

The nice thing about this is that the production deployment process does not require any extra work (I tried it on a page that gave me the LocalVariableTable error, and it worked without issue).

Hopefully there won't be a need for workarounds with Tapestry soon, Howard Lewis Ship has been working on abstracting Javaassist out of Tapestry recently, and I've seen some GAE compatibility commits from him as well.

Next up, try to figure out how to get JDO working with Tapestry!

No comments:

Post a Comment