Struts and Freemarker Liferay Portlet Tutorial

It took me a while to get the Liferay to work with the web frameworks I used before, Struts2 + Freemarker. The sources are available for download in here. I have developed and tested this example with the liferay-5.2.3+tomcat-5.5 version.

Configuring the portlet

Below is the file structure I have used in this example.

In Liferay the portlet is configured with the configuration files in the web/WEB-INF directory. We will start with portlet.xml file.

<portlet-app ... >

    <portlet id="struts-freemarker-hello">
        <display-name>Struts+Freemarker Hello Example</display-name>

        <!-- The view mode namespace. Maps to a namespace in the Struts2 config file. -->

        <!-- The default action to invoke in view mode. -->

The first important thing in the portlet.xml is the portlet id, which is used in the other, liferay specific configuration files. The we have the portlet class which in our case is set to org.apache.struts2.portlet.dispatcher.Jsr168Dispatcher.

We give two init parameters to the portlet class, viewNamespace and defaultViewAction, which tell our portlet what action to initially call from conf/struts.xml.

Next we have liferay-portlet.xml, which for this example just contains the id of our portlet.


And last is the liferay-display.xml where the our portlet is listed under the category example.

    <category name="example">
        <portlet id="struts-freemarker-hello"/>

Making the action

Our struts action is pretty simple, just has 2 methods for the 2 actions we are going to use and getter/setter for the name parameter.

package com.hyperin.sample;
import com.opensymphony.xwork2.ActionSupport;
 * @author heikkiu
public class HelloAction extends ActionSupport {

    private String name;

    public String getName() {
        return name;

    public void setName(String name) { = name;

    public String execute() {
        return SUCCESS;

    public String sayHello() {
        return SUCCESS;

Our actions are defined in the src/conf/struts.xml.

    <package name="sample" extends="struts-portlet-default" namespace="/sample">
        <action name="hello" class="com.hyperin.sample.HelloAction">
            <result name="success" type="freemarker">/pages/hello_form.ftl</result>
        <action name="sayHello" class="com.hyperin.sample.HelloAction" method="sayHello">
            <result name="success" type="freemarker">/pages/hello_response.ftl</result>

Only difference to a normal struts configuration is the struts-portlet-default package we are extending. The results are mapped to the freemarker templates in web/pages. First template hello_form.ftl contains the form.

<form action="<@s.url action="sayHello"/>" method="post">
    <h1>Your name:</h1>
    <input type="text"name="name" value="${name!"Bob Brown"}"/>
    <input type="submit" value="Say hello!"/>

It's pretty plain html with one very important thing: the generation of the form's action with the freemarker built-in <@s action="sayHello"/> taglib. The response template is even plainer.

<h1>Hello ${name}!</h1>
<a href="<@s.url action="hello" name="${name}"/>">Go Back...</a>

Here also the important bit is the url for the back link url <@s.url action="hello" name="${name}"/>, which now has the name parameter given to the url.

Building and running the example

Once you have downloaded the zip, unzip and edit the deploy.dir property in the build.xml to point to the deploy directory under your liferay installation.

<property name="deploy.dir" value="C:/liferay-portal-5.2.3/deploy"/>

After that go to the project root and run the build with ant


After a while the webserver log should tell you that
22:33:17,228 INFO  [PortletHotDeployListener:346] 1 portlet for struts-sample-portlet is available for use

Now you can open your browser and under the Add Application menu you should find a new category called example, which contains the portlet Struts+Freemarker Hello Example. Once you add the portlet to your page you can see it in action.


  1. Hi

    Thanks for the sample.
    It's the clearest explanation I've found and it helped me out.


  2. This is what am looking for. Great job.