I attended a study session about Play! Framework in Osaka. Actually, I hadn't used it, but most of the sessions were for beginners, and quite useful for me. After the sessions finished, we had two hours of free time and everybody did what he liked. Reading books, talking about Play, just chit-chatting etc. I was trying to get a Play app in Scala running on Google App Engine (GAE), and finally succeeded.
I'll write about the steps that I took and also some pitfalls that I experienced. This entry is for beginners who are interested in Play! framework and Scala.
My environment was as follows:
Here are the steps that I took.
Install Play! framework
Download the zip package of Play! and unzip it. Then add
Install Google App Engine SDK for Java
To install it, download the package, unzip it and set GAE_PATH to the directory.
Play! framework's modules are like plugins. We need to install GAE and Play Scala modules by the following commands:
$ play install scala-0.9 ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.2.1, http://www.playframework.org ~ ~ Will install scala-0.9 ~ This module is compatible with: 1.2 ~ Do you want to install this version (y/n)? y ~ Installing module scala-0.9... ~ ~ Fetching http://www.playframework.org/modules/scala-0.9.zip ~ [--------------------------100%-------------------------] 18785.9 KiB/s ~ Unzipping... ~ ~ Module scala-0.9 is installed! ~ You can now use it by adding it to the dependencies.yml file: ~ ~ require: ~ play -> scala 0.9 ~ $ play install gae ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.2.1, http://www.playframework.org ~ ~ Will install gae-1.4 ~ This module is compatible with: 1.1, and GAE 1.4.0 ~ Do you want to install this version (y/n)? y ~ Installing module gae-1.4... ~ ~ Fetching http://www.playframework.org/modules/gae-1.4.zip ~ [--------------------------100%-------------------------] 18187.5 KiB/s ~ Unzipping... ~ ~ Module gae-1.4 is installed! ~ You can now use it by adding it to the dependencies.yml file: ~ ~ require: ~ play -> gae 1.4
You may notice a message about dependencies.yml. We'll do that later.
Create a Play project
$ play new projectfoo --with scala
As I wrote above, we need to write module dependencies in
# Application dependencies require: - play - play -> scala 0.9.1 - play -> gae 1.4
Then, I executed the following command. I'm not sure what it does actually (will check).
$ play dependencies --sync ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.2.1, http://www.playframework.org ~ ~ Resolving dependencies using /Users/k4200/projectfoo/conf/dependencies.yml, ~ ~ play->scala 0.9 (from playLocalModules) ~ play->gae 1.4 (from playLocalModules) ~ ~ Some dependencies have been evicted, ~ ~ play 1.2 is overriden by play 1.2.1 ~ ~ Installing resolved dependencies, ~ ~ modules/scala-0.9 -> /Users/k4200/programs/play-1.2.1/modules/scala-0.9 ~ modules/gae-1.4 -> /Users/k4200/programs/play-1.2.2/modules/gae-1.4 ~ ~ Done! ~
Create an application on GAE
Create an application on GAE. The application name doesn't have to be the same as the project name ("projectfoo" in this case).
Almost there. We need to tie the Play! project (projectfoo) with the app on GAE. This information should be defined in appengine-web.xml, which doesn't exist at first.
The following command starts the app on the local environment and creates the file. After the app is started, you can quit it by Ctrl + C.
$ play run ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.2.1, http://www.playframework.org ~ ~ Ctrl+C to stop ~ Listening for transport dt_socket at address: 8000 17:45:29,443 INFO ~ Starting /Users/k4200/workspace/projectfoo 17:45:29,446 INFO ~ Module gae is available (/Users/k4200/programs/play-1.2.2/modules/gae-1.4) 17:45:29,447 INFO ~ Module scala is available (/Users/k4200/programs/play-1.2.1/modules/scala-0.9) 17:45:30,070 WARN ~ 17:45:30,070 WARN ~ Google App Engine module 17:45:30,070 WARN ~ ~~~~~~~~~~~~~~~~~~~~~~~ 17:45:30,070 WARN ~ No Google App Engine environment found. Setting up a development environement 17:45:30,080 WARN ~ Don't forget to define your GAE application id in the 'war/WEB-INF/appengine-web.xml' file 17:45:30,080 WARN ~ 17:45:30,080 INFO ~ Detected that plugin 'play.modules.gae.GAEPlugin@6708f8e0' disabled the plugin 'play.jobs.JobsPlugin@53dafbaf' the old way - should use Play.disablePlugin() 17:45:30,797 WARN ~ You're running Play! in DEV mode 17:45:30,862 INFO ~ Listening for HTTP on port 9000 (Waiting a first request to start) ... 17:45:47,762 INFO ~ Application 'projectfoo' is now started ! ^C~ ...
Then edit the file. It should look like the following. "gaetestfoo" is the name of the application on GAE, which is different from the project name "projectfoo".
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <application>gaetestfoo</application> <version>1</version> </appengine-web-app>
I encountered several errors before getting the app running. I'll write those error messages and solutions/workarounds here.
GAE SDK isn't installed
~ You need to specify the path of you GAE installation, ~ either using the $GAE_PATH environment variable or with the --gae option
400 Bad Request
This happened when I forgot to modify appengine-web.xml.
java.io.IOException: Error posting to URL: https://appengine.google.com/api/appversion/create?app_id=&version=1& 400 Bad Request app_id GET query string parameter must be supplied. Unable to update app: Error posting to URL: https://appengine.google.com/api/appversion/create?app_id=&version=1& 400 Bad Request app_id GET query string parameter must be supplied.
Cannot load jar
play.Logger niceThrowable: Cannot load jar:file:/base/data/home/apps/s~gaetestfoo/1.35235236581793114/WEB-INF/lib/play-scala.jar!/play.plugins java.lang.NullPointerException at play.scalasupport.ScalaPlugin.<init>(ScalaPlugin.scala:166) at java.lang.Class.newInstance0(Class.java:372) at java.lang.Class.newInstance(Class.java:325) at play.plugins.PluginCollection.loadPlugins(PluginCollection.java:100) at play.Play.init(Play.java:286) at play.server.ServletWrapper.contextInitialized(ServletWrapper.java:74) at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:548) at org.mortbay.jetty.servlet.Context.startContext(Context.java:136) at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1250) at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:517) at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:467) at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50) at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.createHandler(AppVersionHandlerMap.java:202) at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.getHandler(AppVersionHandlerMap.java:171) at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:123) at com.google.apphosting.runtime.JavaRuntime.handleRequest(JavaRuntime.java:260) at com.google.apphosting.base.RuntimePb$EvaluationRuntime$2.handleRequest(RuntimePb.java:9805) at com.google.net.rpc.impl.RpcUtil.runRpcInApplication(RpcUtil.java:422) at com.google.net.rpc.impl.Server$RpcTask.runInContext(Server.java:579) at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:449) at com.google.tracing.TraceContext.runInContext(TraceContext.java:689) at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:327) at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:319) at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:447) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) at java.lang.Thread.run(Thread.java:636)
I'm not sure this is a bug that affects other people, or an issue specific to my environment. I'll look into this when I have time, and send a bug report if need be.
The following command creates .project file for Eclipse.
$ play eclipsify
Then, you need to import the created project into Eclipse.
Error in Scala compiler: typeConstructor inapplicable for <none>
Usually, you're prompted to add it to your project and thus don't see it.
Configure the build path
object Application is not a member of package views
I found a thread about the problem and workaround on StackOverflow. Adding tmp/generated to you Build Path solves the issue.
Personally, I like Lift and have used it for several months now. One of the advantages of Play over Lift is it has good documentation. And, as far as I see from the documentation, Play seems pretty powerful yet easy to start. Also, GAE is a great platform, so combination of Play, Scala and GAE should be fantastic. Yes, it should be.
But, the problem is there's not a lot of information about the combination, or real-world production experiences using them. If I were to use Play for a real product or service, I wouldn't choose GAE. EC2 (or other cloud services) are more straight forward and less trouble in terms of development.
In Scala world, there are some other frameworks than Lift and Play that have come out recently. Exciting, isn't it? Since there's no "best" framework in Scala at this moment, I'll keep watching them for now and choose an appropriate one on a case-by-case basis.