If you're familiar with Clojure web development, you will likely already know most of this tutorial. To save you time, I will add a star (☆) to sections that are unique to GAE.
If you want to try this example at home, you'll need to create an account on GAE and create a project with an application in their standard environment. The standard environment is sandboxed and quite limited, but it's enough to explore GAE.
When you first sign up, there's a tutorial for creating a Java based Hello World application in your project. It's worth your time to go through that, because then you can switch that project over to Clojure mode later.
To create the Clojure project, I started with the default leiningen template. Then I added necessary dependencies to it, and added some additional configuration to the project.clj file that is required for the lein-ring plugin to create a war file. After that, I wrote the application code, and finally I scripted the remaining steps to deploy the application on GAE.
To create my Clojure project, I simply created a new application with the default leiningen application project, and then I added the following dependencies to it.
[ring/ring-core "1.5.1"]Ring is the go-to routing library in Clojure, and you'll need ring-core for that. Additionally, you'll need to interface with the GAE servlet environment once you're running in the GAE environment with ring-servlet. Compojure adds a useful DSL on top of ring routes, and Cheshire is a Clojure wrapper for Jackson, which is a JSON processing library.
[ring/ring-servlet "1.5.1"]
[compojure "1.5.2"]
[cheshire "5.7.0"]
After adding the dependencies, I added configuration the project file for lein-ring. Which is a plugin to help you with ring web development.
:plugins [[lein-ring "0.11.0"]]
:ring {:handler clojure-gae-quickstart.core/app}
The first line adds the lein-ring plugin to the project, and the second line tells it where your ring routes are so you can run your web application in development mode with lein-ring. This is especially useful, because lein-ring will reload your code by default with each request, so you can make changes to the running server without having to restart.
☆ After configuring lein-ring, I added configuration files for GAE to the war-resources/WEB-INF directory.
war-resources/WEB-INF/appengine-web.xml
war-resources/WEB-INF/logging.properties
☆ appengine-web.xml file is a descriptor that GAE uses when you upload your application. You'll need to change the application tag in that file to match the application name that you have configured in the GAE console. The war-resources directory is used by the lein-ring plugin to add additional resources to the war file when you use lein-ring to create a war file.
Next, I added an index.html to the resources/public folder, and I then wrote the code to the clojure-gae-quickstart.core namespace to duplicate the functionality of the Java getting started example. This code is completely independent of GAE, so I was able to test it and debug it with lein-ring. It's not clear if that would be possible if the code relied on GAE services. I may look into that in the future.
☆ Google provides a script called appcfg.sh in the Java application development kit which can be used to upload an exploded war file and deploy your application. So, you must first create the war file and then unzip it and update the version number in the exploded copy of the appengine-web.xml file to something unique, and finally upload it to Google with the appcfg.sh script. I implemented this with a quick and dirty script, make.sh. It probably wouldn't be that difficult to create a leiningen plugin that built upon what is in the lein-ring plugin to do this, but I wasn't ready to commit to that effort this weekend.
So, while this is all encouraging. There is a downside. What prompted me to start investigating GAE was that they made PostgreSQL available. However, it's currently is in a beta state, and not available to applications in the standard environment. (reference)
I'm not particularly interested in MySQL, or interested in cloud vendor lock in with proprietary storage APIs. So right now, this effort goes on the shelf. I might take a look and see if it's possible to develop v.s. their simulator libraries for their services without running in their test server, but right now I'm not sure if I will.
I hope somebody finds this useful.