backing mobile web apps with java?
My latest task at AeroGear team was to do some research on how could we improve the Java server-side support for mobile web apps, but how different is a mobile web app from a normal web app?
Knowing that it would be impossible to cover all the scenarios, I picked one and started digging.
single-page application + rest
Let’s picture this kind of application:
- a single HTML page that loads all the static resources (JavaScript libraries, CSS, fonts, images)
- the main application is written in JavaScript, using frameworks like jQuery, jQuery Mobile, backbone.js, ember.js, etc…
- tons of REST endpoints
Note that you want to have just a little bit of server-side templating/rendering for this single page, at least to have a pretty URI (instead of linking to the .html file directly).
We cannot afford to talk about web stuff without taking a look at stuff made for the Ruby language, so let’s get inspiration there!
Ruby
My gut reaction says that this is a perfect case for using Sinatra, a very terse ruby framework:
sinatra
The main page => myapp.rb
require 'rubygems'
require 'haml'
require 'sinatra'
get '/home' do
haml :home #will render views/index.haml
end
The template => views/index.haml
!!! 5
%html
%head
%title Home
%body
%h1 Hello World
If you don’t know haml, is a markup for generating HTML with a terse syntax (I personally don’t like it that much, but it helps on saving keystrokes for a blog post).
Small, eh? What about seeing how could we do the same with Rails?
Ruby on Rails
On Rails, we have centralized routing, so we would have an entry on the routes file => config/routes.rb
match '/home' => 'home#index'
This route will ensure that when we access /home
the framework will call the index
method on the HomeController
class. Yay to conventions! => /app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
end
end
Again conventions making things just work, this will call /app/views/home/index.html.haml
(if you have haml support enabled, naturally)
Before someone starts complaining that this is cool but not Java, let me remind you that this works perfectly on the JVM thanks to JRuby. You just need to choose a server like TorqueBox and you’re all set.
How can we have something like this, but for java?
Java
Since the Java language gained metadata support through annotations, lots of developers started (ab)using it for a myriad of tasks, in special for declarative configuration.
As expected, almost everything has its pros and cons, and this is no different here. Several web frameworks, like Struts, Spring MVC, VRaptor used annotations to setup their routes instead of having extensive XML files. This is very good for the majority of cases, but can hurt when you have many different routes (try to imagine an application with 200+ endpoints).
What if we went back to having centralized routing again?
As I was digging more the code of some Java MVC frameworks, I started to see a pattern, where they usually grow to supply their userbase needs, and start being a little heavyweight. I really wanted something minimalist for AeroGear server-side support, so I started gathering ideas together with our team, and laid out some basic prerequisites to begin with.
- very fast bootstrap time
- centralized routing
- POJO-based controllers (nothing new here)
- immutable parameter beans support
- first-class CDI support
- pluggable view resolving
Note that all the cited frameworks do an amazing job; here we are making architectural tradeoffs to achieve some specific goals.
This research led to this PoC
disclaimer: as this is a PoC, nothing is written on the stone - everything is subject to change, and the code is nowhere production ready! this is just a bunch of ideas!
very fast bootstrap time
For this PoC, I avoided using annotation metadata, to reduce the boot times by avoiding classpath scanning. It can prove itself a premature optimization, but I’m happy with the results for now.
centralized routing
We know that the Java language is verbose by nature, but at the same time, it has superb IDE support, so the initial approach for configuring routes is to use programmatic routes, with help of a nice internal DSL:
public class Routes extends AbstractRoutingModule {
@Override
public void configuration() {
route()
.from("/home")
.on(GET)
.to(Home.class).index();
}
}
Note that this code is autocompletion aware and refactor-friendly!
pojo-based controllers
The Home
controller is a POJO in all its glory:
public class Home {
public void index() {
}
}
first-class CDI support
Having full CDI support means that you can inject other objects in your controllers, as usual:
public class Products {
@Inject
public Account(ProductRepository repository) {
...
}
}
immutable parameter beans support
Making immutable objects when possible is a good thing:
public class Car {
private String color;
private String brand;
public Car(String color, String brand) {
this.color = color;
this.brand = brand;
}
public String getColor() {
return color;
}
public String getBrand() {
return brand;
}
}
You can use immutable beans straight away as controller parameters, thanks to Iogi:
public class Store {
public Car save(Car car) {
return car;
}
}
This can be populated by putting a route to it (preferrably via POST, of course):
route()
.from("/cars")
.on(POST)
.to(Store.class).save(param(Car.class));
And you can use a simple html form for it, by just following the convention:
<input type="text" name="car.color"/>
<input type="text" name="car.brand"/>
The car object will be automatically populated with the provided values - note that it supports deep linking, so this would work fine too:
<input type="text" name="car.brand.owner"/>
All the intermediate objects are created automatically.
pluggable view resolver
When you access /home
, the page /WEB-INF/pages/Home/index.jsp
will be rendered by default (by following the convention of /WEB-INF/pages/Controller Class Name/method name.default extension)
Currently only JSP files are supported but I’m working on providing haml support too through the aerogear-haml.
You can find the PoC sources here.
Thoughts?