Interactive HTTP Server Tutorial
This tutorial introduces the the final of the 3 web page instrumentations: #{link}
The other two instrumentations ( <!-- {section} --> and ${property} ) focus on dynamic content for rending to the web client. This final instrumentation is focused on processing the requests back from the web client.
Additionally covered in this tutorial is:
Also touched on are some of OfficeFloor's dependency injection capabilities.
The below example for this tutorial will implement the ability to add items to a list along with clearing the list. Different parts of the template will be shown based on whether there are items in the list.
The following shows the tutorial's example web page with no items in the list:

On adding two entries, the page will look as follows:

The below is the content of the Template.html.
<html>
<body>
<!-- {listItems} -->
<table border="1">
<tr>
<td>Name</td>
<td>Description</td>
</tr>
<!-- {items} -->
<tr>
<td>${name}</td>
<td>${description}</td>
</tr>
<!-- {endItems} -->
</table>
<p><a href="#{clear}">Clear</a></p>
<!-- {noItems} -->
<p>Currently no items in list.</p>
<!-- {endListItems} -->
<form action="#{addItem}">
Name: <input name="name" type="text" />
Description: <input name="description" type="text" />
<input type="submit" value="Add" />
</form>
</body>
</html>
This tutorial will use the template section divisions to enable controlling what is rendered along with the #{link} to enable user interactions.
The Template.html has its logic provided by the TemplateLogic class.
The first aspect of this class is the annotation to indicate that it should be bound to the HTTP Session.
@HttpSessionStateful
public class TemplateLogic implements Serializable {
private List<Item> items = new LinkedList<Item>();
The annotation is a flag to WoOF that the TemplateLogic object for the page should be stored in the HTTP Session. This will then make the list of items available across requests. The class implements Serializable as must all objects bound to a HTTP session.
As the items are now stored across HTTP requests, the items must be exposed for rendering to the template. The below method exposes the Item objects for being rendered.
public Item[] getItems() {
return this.items.toArray(new Item[this.items.size()]);
}
With the above method the template can be rendered to display the items.
Initially there will be no items in the list so the page provides the form submission to add an item.
Looking back at the template the action url of the form is actually #{addItem} . On submitting the form, OfficeFloor will invoke the below method via naming convention (same name).
public void addItem(Item item) {
this.items.add(item);
}
Notice that the method has a single parameter Item. The Item class is annotated as follows which identifies to WoOF that the object should be loaded with the corresponding HTTP parameter values.
@HttpParameters
public class Item implements Serializable {
private String name;
private String description;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
}
The following steps therefore occur on submitting the form:
This relieves writing plumbing code and allows focus on handling the values submitted by the user (in this case adding the item to the list). It also makes for cleaner and easier to unit test code.
Once the addItem method returns, WoOF will by default re-render the template back to the user. It is possible to direct flow to other templates however this is beyond the scope of this tutorial.
Along with adding an item to the list the template also provides the ability to clear the list of items. Clicking on the #{clear} link invokes the following method.
public void clear() {
this.items.clear();
}
Again by default, the template will be re-rendered once the clear method returns.
OfficeFloor achieves this simple interactive programming model by subsituting into the rendered page a unique url which it can map back to the corresponding method. The method is matched by its name and is free to have any parameters it requires (enabled by OfficeFloor's dependency injection). For example in more complex applications the handling methods may include a parameter for a DataSource or EntityManager to enable database interaction rather than just storing content into the object. This however is beyond the scope of this tutorial.
Now that the user is able to add and clear items, control is necessary to only render certain parts of the template.
In this case:
To provide control over the sections rendered in the page, the following interface is created.
@FlowInterface
public static interface PageFlows {
void getNoItems();
void endListItems();
}
The interface is annotated to tell WoOF it provides flow links. The methods of the interface correspond by name to the template sections and the template object's methods.
The template logic for controlling the rendering of sections is as follows:
public void getListItems(PageFlows flows) {
if (this.items.size() == 0) {
flows.getNoItems(); // skip to getNoItems
}
}
public void getNoItems(PageFlows flows) {
if (this.items.size() > 0) {
flows.endListItems(); // skip to endListItems
}
}
For the above methods, WoOF will invoke them before rendering the corresponding template sections. On invoking the method, WoOF will generate an object implementing the PageFlows interface that will allow changing the next section to be rendered. Should no method be invoked then the next section is rendered as per the template. Should a method be invoked then the corresponding section by the interface method name becomes the next section to be rendered. In this case:
Controlling rendering of the template like this means that the code is not dependent on any framework API's and subsequently allows easier unit testing (via mocking the interface).
This style of controlling the rendering works well with OfficeFloor's dependency injection. As the methods match based on naming and not on method signature (i.e. parameters are ignored), it allows for the interface and template logic class methods to have different parameter lists. The result is that dependencies are used only as necessary and also makes for adding/removing dependencies easier. For example, the following shows the changes necessary to make a DataSource available:
public void getNoItems(PageFlows flows, DataSource dataSource) {
// code requiring DataSource
}
OfficeFloor will happily inject the additional DataSource dependency on invoking the method. The call from getListItems will not need to be aware that the implementing method has had a change in dependencies - especially if later it might change again to call a web service.
This ability to dependency inject into methods is only the start of the functionality contained within OfficeFloor, as this starts to touch on the Job Based Architecture at it's heart.
The unit test requests the various URL's exposed from the template.
private final HttpClient client = new DefaultHttpClient();
public void testPageInteraction() throws Exception {
// Start server
WoofOfficeFloorSource.main();
// Request the template
this.doRequest("http://localhost:7878/example");
// Add an item
this.doRequest("http://localhost:7878/example.links-addItem.task?name=Daniel&description=founder");
// Clear the items
this.doRequest("http://localhost:7878/example.links-clear.task");
}
private void doRequest(String url) throws Exception {
HttpResponse response = this.client.execute(new HttpGet(url));
assertEquals("Request should be successful", 200, response
.getStatusLine().getStatusCode());
response.getEntity().writeTo(System.out);
}
The next tutorial looks at navigating between pages (templates).