How to build a period selector
This example assumes you've already created a web module. All directory references in the examples are relative to the start directory of the created web module. The Java code does not show import statements, as we assume you have an IDE like Eclipse to handle this.
In this example we show how you can produce a simple drop down box showing a set of periods. The example demonstrates how to use the DHIS 2 API and display data on a web page.
WebWork is centered around Actions, Java classes written by the web developer. These actions function as the controller in the MVC-pattern: They prepare data for a View, for example a Velocity template. In our case, the action will retrieve a set of periods from the service layer of DHIS 2 and store these in variables which a VM-file can access.
Create the action
Actions should be placed in the src/main/java/org/hisp/dhis/<module-name>/action/ directory and the package name of the class should similarly be org.hisp.dhis.<module-name>.action
In our example, we'll make a PeriodAction class, which initially looks like this:
public class PeriodAction
implements Action
{
public String execute()
{
return SUCCESS;
}
}
The Action interface tells WebWork that this is an action class and also gives the programmer access to some constants. To begin with, this action class does absolutely nothing. When run, the Webwork will call the execute method on the class. The method will return immediately. The SUCCESS constant is retrieved from the Action interface.
Create the template
The template will be very simple, displaying a select box for periods and a label for the control. For now, we'll just make the skeleton:
<p>
<label for="periodSelect">Select period:</select>
<select id="periodSelect" name="period">
<!-- Will put more here later -->
</select>
</p>
This code contains three HTML elements. The paragraph, the label and the select box.
The label element has a neat effect in browsers. If the user clicks the label, the associated form control will be activated, in this case the select element with the id "periodSelect". This means people with bad motor skills have a larger area to "hit" with their cursor, promoting accessibility.
The select element has the id "periodSelect" and the name attribute is set to "period". When the associated form is submitted, a CGI-paramter "period" will be sent to the server. The value will depend on which option element is selected in the select box.
All templates should go in the src/main/webapp/<module-name>/ directory.
We save it as period.vm
Configure the action
Now we need to tell WebWork about the action by mapping the action class to an URL pattern, and the result to the Velocity template. This is done in the file src/main/resources/xwork.xml. The basic xwork-file will look like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xwork PUBLIC "-
"http:>
<xwork>
<include file="dhis-web-commons.xml"/>
<package name="dhis-web-dataentry" extends="dhis-web-commons"
namespace="/dhis-web-dataentry">
<!-- Your action mappings go here! -->
</package>
</xwork>
We now want to add an action called "index". This action will be available as "http://my.server.here:8080/<web-module>/index.action". The action should run the PeriodAction class, and send the results to the period.vm template file. The mapping looks like this:
<action name="index" class="org.hisp.dhis.<module-name>.action.PeriodAction">
<result name="success" type="velocity">/main.vm</result>
<param name="page">/<module-name>/period.vm</param>
</action>
This mapping states that there should be an index action, mapped to the PeriodAction class. If the type of result is success, ie if the execute method of the action class returns SUCCESS, the /main.vm file should be rendered by Velocity.
So why /main.vm? And what's the param element about? /main.vm refers to the file dhis-web-commons-resources/src/main/webapp/main.vm (in a different module than your own). This file contains the overall layout used by DHIS 2, the title bar, the module menu, the CSS stylesheet references, the core JavaScript files and so on. This template will include the contents of the VM file sent as the page parameter. In this case, we send our own period.vm file. The content gets included so that it appears in the main part of the resulting page, under the module menu.
This is why period.vm does not contain any html, head, body elements or anything similar. These are all defined in main.vm.
Get the period data
We communicate with the database through a framework called Hibernate. This allows us to make the application database-independent, as Hibernate handles this for us. Furthermore, Hibernate gives us normal Java objects rather than SQL result sets.
Instead of running database querries directly into the database, we've defined an API for DHIS 2, a set of Java classes called services to get and manipulate data from the database. For each kind of model object in the system, there is a Service interface. For period objects, this is PeriodService. For data elements, this is DataElementService, etc.
We can now add some code to the Action class, to demonstrate how this would work.
public class PeriodAction
implements Action
{
private PeriodService periodService;
private Collection<Period> periods;
public void setPeriodService( PeriodService periodService )
{
this.periodService = periodService;
}
public Collection getPeriods()
{
return periods;
}
public String execute()
{
periods = periodService.getAllPeriods();
return SUCCESS;
}
}
When the action is run, the getAllPeriods() method is called on the periodService. The service will call methods in Hibernate, which will again create necessary SQL queries. When the queries are complete, Hibernate translates them into Java objects, in this case Period objects and return them to the periodService. The service sends them to action class, which stores them in a periods variable.
Note that the periodService has a setter and that the periods property has a getter. This is because the periodService will be injected into the object at runtime and the period property will be read by Velocity. More on that later.
Configure the PeriodService bean
In the example above, the periodService class appears magically. Getting this code working requires a little bit of work. In DHIS 2 we use the Spring framework to wire objects together. We use a method called dependency injection to get an instance of the PeriodService interface into the PeriodAction instance. To do this, we must define a Java bean. We do this in an XML file which initially looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http:
xmlns:xsi="http:
xsi:schemaLocation="
http:
<!-- Beans go here! -->
</beans>
The file is saved as src/main/resources/META-INF/dhis/beans.xml. The path and name must be exactly like this in order for Spring to find it.
We want to add a bean mapping for the PeriodAction class to an instance of the PeriodService interface. We add a bean like this:
<bean id="org.hisp.dhis.<module-name>.action.PeriodAction"
class="org.hisp.dhis.<module-name>.action.PeriodAction">
<property name="periodService"
bean="org.hisp.dhis.period.PeriodService"/>
</bean>
The id of the bean is the same as the package and class name of the action class. This convention allows Spring to talk automatically with WebWork when setting up beans. The property refers to the name of a property in the action class, ie
private PeriodService periodService;
The property must have a public setter for this to work. When the system runs, Spring will recieve an instance of the action class and inject the bean with the id "org.hisp.dhis.period.PeriodService". This bean is defined elsewhere in the system, and is available through the dependencies defined in the project's pom.xml file.
Note that the id of a bean is always the package and class name of it's interface.
Improve the template
Ok, so we've configured the bean and we've retrieved the periods in our action class. The periods are stored in a variable periods, with a public getter method. This means that the property becomes available to Velocity. We can now write a little bit of template code, using this variable. All variables begin with "$", and all public variables from the action class can be accessed from Velocity.
<p>
<label for="periodSelect">Select period:</select>
<select id="periodSelect" name="period">
#foreach ( $period in $periods )
<option value="$period.id">$period.name</option>
#end
</select>
</p>
We see that the $periods variable is available, pointing to the periods property of the action class. Velocity has built in support for collections, which means we can iterate through a Collections object using the foreach instruction. Velocity uses OGNL, Object Graph Navigation Language, which means you can "dot" your way through the properties of objects, i.e.
means the name property of the object referenced by the $period variable. This works if the name property is public or has a public setter. OGNL will translate $period.name to a call to period.getName().