Thursday, January 3, 2013

Building a web application on GWT and Java AppEngine

If you're about to build a website running on Google AppEngine Java, with a backend using JDO entities and a fronted primarily on Google Web Toolkit, you might find the following tips handy. This is a summary of hurdles I faced (and crossed!) while trying to set things up.

First, the technology stack I will be talking about:

  • Google AppEngine Java SDK 1.7.4
  • JDO persistence (datanucleus-apppengine v2.1.1, datanucleus-api-jdo-3.1.1)
  • RequestFactory 2.5.0
  • Gin 2.0
  • Guice 3.0 (With guice-servlet)
  • GWT 2.5.0
  • GWT Editor framework

Now, the tips. Each of these took me considerable time to figure out. Wherever possible, I've tried to link through to the sites/suggestions that got me through.

  • Resolving ClassPath issues (ClassNotFoundException, NoSuchMethodException, No implementation for ... was bound)
    • By and large, the most common problem you may run into, especially if you're managing dependencies within your IDE(E.g. Eclipse), is ClassPath errors.
      There are plenty of sites explaining the difference between all of these and why they happen. But
      here's a site that helped me troubleshoot this on AppEngine dev mode:
      The beauty about his approach is that he uses a "Listener" that can be added to your web.xml, and automatically run before any request.
    • Also, for some libraries, order import matters. E.g. GuiceInjectorBindings
  • Setting up JDO
    • AppEngine Documentation is pretty good on the basics of JDO. However, the documentation is misleading on Un-owned relationships and Many-to-Many relationships in DataNucleus v2- it suggests using List<Key> or Set<Key>. The DataNucleus API, on the other hand, will conveniently handle your list of objects. If you're using a Many-to-Many relationship, use Set<MyObj>, or make sure you specify an @Order annotation on your column. (List<MyObj> didn't work for me, so I failed back to using a Set, and having an attribute for client-side ordering)
  • Request Factory, and GWT Editor Framework
    • If you followed the Getting Started with RequestFactory guide, as-is and are closing your EntityManager or PersistenceManager for every single method(such as persist() or remove()), then you're not following the OpenSessionInView (Session-per-request) pattern. GWT Editor framework will not be able to handle your "related entities" in this case.
      Your options are: 1) use Guice for creating a Request-scoped PersistenceManager, or 2) switch away from JDO, and move to Objectify or Twig or some other interface that already takes care of a lot of persistence logic for you.

Guiced-up GWT RequestFactory Gotchas

 

First of all, all due credit to Etienne for his amazing blog post, as well as the super simplified library that helped me get service-side Guice setup properly with GWT RequestFactory.

There were a few extra issues that I ran into, that took me some time to figure out. So I present an example of how to put the Locators together for convenient Guice'ing.

Technology assumptions:

  • AppEngine Java
  • GWT RequestFactory
  • JDO entities
  • Guice for server-side DI

This post assumes you're already familiar with GWT RequestFactory setup as well as with Guice. It also assumes you'll follow this along-side Etienne's post. Also, I've tested this with GWT 2.5. Additionally, my primary motivation for setting up Guice, was to get a Request-scoped PersistenceManager for JDO, or rather implement the OpenSessionInView pattern.

  1. Proxy locators and service locators.
    The basic GWT RequestFactory guide provided by Google mentions two ways of setting up Proxy and Request interfaces
    • a simple approach using static methods in your Entity
    • use of Locators supporting instance methods in your Dao.

      For using Guice and OSIV pattern, you have to follow the follow this 2nd option. And here's why:

      Guice will be injecting the request-scoped PersistenceManger for us, like this:

      public class MyObj {
          private Provider pmp;
          @Inject
          public MyObj(Provider pmp) {
            this.pmp = pmp;
          }
      }

      Now, if you're following the "simple approach", of course, using static methods is not good from a testability point-of-view. But even otherwise, to use a "PersistenceManager" in the entity, you would have to @Inject it. This means that your Entity will no longer have a "default constructor", and you'll have to provide it a PersistenceManager instance wherever you have to instantiate it. It also means you can no longer have a
      Builder for your entity.

      Hence, you should follow the second approach, and set up locators and DAO services as mentioned in Etienne's post.


  2. Setting up Guice-servlet
    To make the best out of Guice, you might want to bring down web.xml to its bare minimum. Here is what I would recommend bringing down the web.xml file to:


    • Guice filter config
      This is evident. Without this, your Guice won't get triggered.

    • welcome-file-list (files for static serving) 
      This is what gets served if the URL path doesn't match any pattern.

    • security contraints (E.g. for AppStats)
      Now.. where does everything else go? All "servlet", "filter" and "listener" mappings go into a Guice "ServletModule". In case your web.xml specifies specifies <param-value>'s, these can be
      put into a HashMap and passed to their respective filters(See Initialization parameters).


  3. Setting up Request-scoped PersistenceManager

    For setting up your PersistenceManager with Guice, I recommend Yaniv Kessler's blog post. As he himself puts it - it's good for prototyping and small projects. As the project grows, however, a better framework like guice-persist is recommended. When I last checked Guice-persist didn't offer direct support for JDO. Lets hope this comes soon.


Finally, if you're not tied down to using JDO, David Chandler's blog posts addresses how to configure GWT RequestFactory with Objectify, where you won't have to worry about OpenSessionInView, and perhaps can avoid Guice for DI on the server-side altogether.

For information on implementing client-side GIN, you may also want to see this post by Stefan Rock.