Java 6 has been the de facto in Java world for many years but that is changing. According to a recent study by ZeroTurnaround 35% developers use Java 7 on Java EE and already 65% on Java SE. Around 7% are already using Java 8 on Java SE. However, one PITA in Java development has been long deployment times - especially painful when developing frontend stuff. We have been using ZeroTurnaround's fabulous JRebel for many years and couldn't think of developing Java applications without it. Of course OSGi with its component model is giving another way out of long deployment times. Still many people are longing for a more lean way of developing, like Django and Ruby on Rails provide. Could that be possible in Java world?
One answer to that question is Play Framework. It provides a similar development path as Django and RoR for Java and Scala developers. Play 1.0 was built on Java but Play 2.0 is already fully built using Scala. Of course still providing Java compatibility. Play promises to be light, stateless and web-friendly framework. Also, its future seems solid as Typesafe has picked it as their Reactive Platform. One of the founding members of Typesafe is Martin Odersky, known for developing Scala language.
First I created a new play project:
and started planning the API through routes. So I opened
conf/routes file and added the following routes to it:
This enables me to request all movies, filter the movies by year, get specific movie and get a list of years for which we have movies. So very simple API, no POST-methods or anything else relating to inserting data, just getting movie-objects from database.
Next I ran into Slick and it seemed like an awesome way to define data models and query the database using plain Scala. So no writing SQL involved. I created a Movies.scala file to app/models folder and modeled out a basetable for movie and a movies table. Our Movie-object has id, name, year, imdb id(for linking to imdb) and poster url(url to movie poster image file). Posters are from MoviePosterDB which allows everyone to download 300 pixels wide posters for free.
This is pretty basic definition, a similar example can be found in Slick's documentation. This enables me to actually add some data to database. I'm using the included H2 in-memory database here for making it simple. Here are the needed dependencies for
Next I will add some data using the
Global.scala file in the app folder:
You can see how easy it is to add data. The call to
movies.ddl.drop is just a safe measurement to clean the db and make sure it's always updated when doing development. Normally we would just need the
Now that we have created the models and have added some data, let's configure some actions for the routes and see if we can get JSON out from our API. To be able to query our data we need some functions to do the queries so let's add those to
app/models/Movies.scala inside the
These new functions enable us to get a single movie object, get all movies or get all movies for a specific year. Next I added the actions to
Now we can test how all these work by firing up play server
play run. After making the first request to
http://localhost:9000 we should get a question asking us to apply database evolutions, this will add all the movies into the database. Next, let's request movies using curl for demonstration purposes:
We can see our API is working as intended and providing list of all movies, movie by id and movies filtered by year. As the API is now in its initial working state we can move on to implementing the UI with Angular. For that we need to add some dependencies to our
And a new route for WebJars to first line of
I'm using WebJars to provide our frontend dependencies. Webjars-play will provide some helpers and integrate RequireJS support for our project. Then we have of course Angular and for making it look pretty, we have Masonry and Foundation. After this we need to setup
views/index.scala.html for dependencies and angular wiring. Here's a snippet of the most important things:
ng-view tells where are angular views are located and the
@Html...-helper inserts RequireJS stuff to our page.
So let's get working. Below I've listed the files that are important for our angular implementation. There are also other files listed in the github-repository and those do contribute to the UI look but they don't define any functionality so I haven't listed them here.
So here we use RequireJS to define which modules we need and then we bootstrap angular and define its modules. Followed by defining basic routing for our UI. We have
/movies for listing all movies and
/movies/:id for getting a page for a single movie. Ok, so next let's define a service for getting the JSON data from our API in the
So we have a function for getting all movies or optionally requesting movies from a specific year. To use this data we need to define a controller that connects to a view. Let's do this in
Here we use angular's
$routeParams to read the optional year-parameter and then call our
moviesAPIservice. If the call is successful we provide the movies-list to our view. The
$scope variable provides access between controllers and views and view will get all data from it.
To finish up let's edit the
partials/movies.html to provide us viewable results.
Now, if you open your browser you should be able to see that
http://localhost:9000/ redirects to
/#/movies and shows you a list of movies. This of course looks ugly and needs fine tuning, but it should show you how fast you can build a JSON API using Scala and Play and providing user interface with Angular. There is only a tiny hint of Angular magic here, check for example how search is implemented and you will be surprised (hint: it's just a one input-element).
You can check a demo application that is hosted on Heroku. It might take a couple seconds to spin up as its hosted on the free tier which spins down every hour. The source code is available at github which includes all the rest code for single movie page and implementing search and year filters. You can find me also on Twitter if you have something to ask. Special thanks to Timo Hanhirova for helping me with the Slick stuff.