How to Develop an Efficient App using Juzu and Ajax
In this blog I show how we should develop efficient applications for eXo Platform using Juzu and Ajax. The application is quite simple and is a rehash of the Who Is Online gadget. After reading this you will understand how to develop application using the Ajax programming model which is easy to use and very powerful.
In this quick blog I will show how to we can produce simple and efficient gadget like applications with Juzu. I took as example the Who Is Online gadget eXo Platform provides. I will not implement of course the business rules for this gadget or the look and feel, however I will show how we can create an application that is efficient, simple and tested.
The main change from the original gadget is to leave the Rest programming model to use the Ajax programming model:
- Rest: a pure JavaScript application that uses Rest services to retrieve JSON data and build the UI in the browser
- Ajax: the client ask the server to provide markup and the client updates a portion of the page with the fragment
Both programming model uses JavaScript, however Ajax applications use usually than Rest applications. So why should we chose Ajax over Rest ? There are several good reasons in this use case
- Ajax applications create markup on the server where most of the services, in the same memory space.
- The first time the application is displayed, Ajax applications can render the entire markup in the initial response, Rest applications can’t. It means that page loading is faster because the client does not have to connect to the server.
A
Beans
Juzu applications can use Javabeans, in our application we will make an WhoIsOnline bean with a single method for retrieving the friends of a user:
public interface WhoIsOnline { Collection<User> getFriends(String userId); }
This bean is just a facade in front of the real services, it does not aim to be the service itself. The motivation is that we can provide several implementations for the bean, in particular we will provide a mock implementation for testing purposes.
Who is online ?
Now we can display who is online in our application with a Juzu controller:
public class Controller { @Inject WhoIsOnline whoIsOnline; @Inject @Path("index.gtmpl") Template index; @View public Response.Render index() { Collection<User> friends = whoIsOnline.getFriends("me"); return index.with().set("users", friends).render(); } }
We need two templates for rendering index.gtmpl and users.gtmpl, the former including the later (we will see the reason later):
The users online are: <div class="users"> #{include path=users.gtmpl}#{/include} </div>
The users.gtmpl templates iterates over the users and display them in a list:
<ul> <% users.each { user -> %> <li>${user.firstName} - ${user.lastName}</li> <% } %> </ul>
Mocking the users
Finally we need to provide an implementation for the WhoIsOnline interface. We will use mock, i.e a simple implementation of the interface that does not require a backend. The goal is to test the UI and in this case using mock is great because the setup is very minimal:
@Singleton public class MockWhoIsOnline implements WhoIsOnline { private ArrayList<User> friends = new ArrayList<User>(); public Collection<User> getFriends(String userId) { friends.add(new User("First-" + friends.size(), "Last-" + friends.size())); return friends; } }
This simple implementation add a new friend to the list each time the getFriends method is called.
Testing the app
Now we will focus on testing the application with a JUnit test:
@RunWith(Arquillian.class) public class WhoIsOnlineTestCase { @Deployment public static WebArchive deployment() { WebArchive war = Helper.createBasePortletDeployment(); war.addAsWebInfResource(new File("src/main/webapp/WEB-INF/portlet.xml"), "portlet.xml"); war.addAsWebResource("whoisonline/jquery-1.7.1.min.js", "jquery-1.7.1.min.js"); // Use spring.xml with the MockWhoIsOnline bean war.addAsWebInfResource("whoisonline/spring.xml", "spring.xml"); return war; } @ArquillianResource URL deploymentURL; @Drone WebDriver driver; }
This project uses several framework that will setup an embedded Tomcat with the GateIn Portlet Container:
- The @RunWith(Arquillian) tells JUnit to run the test with the Arquillian framework
- The deployment() method packages a war file that contains the application, it is invoked by Arquillian after is started Tomcat
- The driver is a Selenium WebDriver wrapper, the @Drone annotation tells Arquillian to inject it in our test
- The deploymentURL will be the base URL of the web application in Tomcat, it will be injected by Arquillian after the application started
Ajax dude
So now our application shows an initial page with the user’s friends we will use Ajax to refresh the page periodically and refresh the page.
Let’s first add a new method to our controller that returns the list of users as an ajax resource:
@Inject @Path("users.gtmpl") Template users; @Ajax @Resource public Response.Render users() { Collection<User> friends = whoIsOnline.getFriends("me"); return users.with().set("users", friends).render(); }
Now we use the users template directly from the controller and this fragment will be returned to the JavaScript part of our application:
$(function() { var refresh = function() { $(".users").each(function() { $(this).jzLoad("Controller.users()"); }); }; // Wait 1/2 second (not realistic of course) // And we should use setInterval with 60 seconds setTimeout(refresh, 500); });
The JavaScript code is quite trivial but there are two important points here
We use the jzLoad function instead of the jQuery load function : the jzLoad is a jQuery plugin provided by Juzu that allows to specify the reference to a controller instead of an URL. Under the hood Juzu will wrap the HTML with a block that contains the URLs.
The other point is that we don’t need any single script tag in the HTML. Instead we rely on the users CSS class to add behaviour, there are several key benefits to this
- The code is in a self contained JavaScript file whoisonline.js and there are not script tag in our application. The whoisonline.js can be cached by the browser
- The same JavaScript code works regardless of the number of the portlet we put on the page
- In eXo Platform 4 we can serve this code as a JavaScript module and benefit from the advanced modularity provided by GateIn 3.5
Last but not least we provide the code for testing the Ajax load:
@Test @RunAsClient public void testIndex() throws Exception { driver.get(deploymentURL + "/embed/WhoIsOnline"); WebElement p = new WebDriverWait(driver, 10).until( new ExpectedCondition<WebElement>() { public WebElement apply(WebDriver input) { List<WebElement> users = input.findElements(By.cssSelector("div.users ul li")); if (users.size() > 1) { return input.findElement(By.cssSelector("div.users")); } else { return null; } } }); List<WebElement> users = p.findElements(By.tagName("li")); Assert.assertEquals(2, users.size()); }
We use Selenium WebDriver until method that will block until more than one li tag is available. This will happen after the first Ajax refresh, if it does not happen it means there is a bug and after 10 seconds the until method will fail and make the unit test fail.