Struts2 OGNL

Filed Under: Struts 2

Struts2 OGNL is the expression language where OGNL stands for Object-Graph Navigation Language. OGNL is tightly coupled in Struts2 and used to store form parameters as java bean variables in ValueStack and to retrieve the values from ValueStack in result pages.

Struts2 OGNL

Struts2 OGNL performs two important tasks – data transfer and type conversion.

OGNL in Struts2 takes the request parameters from the servlet request and transfer it to corresponding java variable.

Since we get request params as String but java bean variables can be String, int, array, list or any custom object, type conversion is also an important task and OGNL takes care of type conversion through it’s built-in type converters.

Struts2 OGNL is flexible and we can easily extend it to create our own custom converter class. We will first look into the OGNL usage with basic data types such as String, boolean, int, arrays and lists. Then we will create our own converter class for a custom java bean variable.

Struts2 OGNL Example

Our final project structure for Struts2 OGNL example looks like below image.

Struts2 OGNL, OGNL in Struts2

Struts 2 Configuration Files

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>Struts2OGNLExample</display-name>

	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

</web-app>

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>Struts2OGNLExample</groupId>
	<artifactId>Struts2OGNLExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<dependencies>
		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-core</artifactId>
			<version>2.3.15.1</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
		<finalName>${project.artifactId}</finalName>
	</build>
</project>

struts.xml


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.devMode" value="false"></constant>
<constant name="struts.convention.result.path" value="/"></constant>
<package name="user" namespace="/" extends="struts-default">
	<action name="home">
		<result>/home.jsp</result>
	</action>
	<action name="welcome" class="com.journaldev.struts2.actions.WelcomeAction">
	<result name="success">/welcome.jsp</result>
	</action>

</package>

</struts>

Configuration files are self understood and they are just to configure our application to use Struts 2 framework.

Struts2 OGNL Example Model Classes


package com.journaldev.struts2.model;

public class Data {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	
}

package com.journaldev.struts2.model;

public class Rectangle {

	private int x;
	private int y;
	
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	
	
}

package com.journaldev.struts2.model;

import java.util.Date;
import java.util.List;
import java.util.Map;

public class MyJavaBean {

	private String name;
	private boolean flag;
	private Integer age;
	private Date date;
	private String[] stocks;
	//roles array needs to initialize because it's used with index in form
	private String[] roles = new String[5];
	
	//do not preinitialize lists or any collections
	private List<Data> usersList;
	private List<Data> fruitsList;
	private Map<String, Data> usersMap;
	
	//custom type converter example
	private Rectangle rectangle;
	
	public List<Data> getFruitsList() {
		return fruitsList;
	}
	public void setFruitsList(List<Data> fruitsList) {
		this.fruitsList = fruitsList;
	}
	public List<Data> getUsersList() {
		return usersList;
	}
	public void setUsersList(List<Data> users) {
		this.usersList = users;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public String[] getStocks() {
		return stocks;
	}
	public void setStocks(String[] stocks) {
		this.stocks = stocks;
	}
	public String[] getRoles() {
		return roles;
	}
	public void setRoles(String[] roles) {
		this.roles = roles;
	}
	public Map<String, Data> getUsersMap() {
		return usersMap;
	}
	public void setUsersMap(Map<String, Data> usersMap) {
		this.usersMap = usersMap;
	}
	public Rectangle getRectangle() {
		return rectangle;
	}
	public void setRectangle(Rectangle rectangle) {
		this.rectangle = rectangle;
	}
	
}

MyJavaBean is the action bean class that we will use, notice the variable of type Rectangle. Since it’s a custom class, we need to implement our own converter class for this. We will look into this later.

Struts2 OGNL Example Action Class


package com.journaldev.struts2.actions;

import com.journaldev.struts2.model.MyJavaBean;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class WelcomeAction extends ActionSupport implements ModelDriven<MyJavaBean>{

	public String execute(){
		return SUCCESS;
	}
	
	private MyJavaBean bean = new MyJavaBean();
	
	@Override
	public MyJavaBean getModel() {
		return bean;
	}

}

Action class just returns the success page, there is no logic done here.

Struts2 OGNL Example Result Pages

home.jsp


<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Home Page Form</title>
</head>
<body>
<h3>Struts 2 OGNL Examples</h3>
<s:form action="welcome" method="post">
<table>
<s:textfield name="name" label="Name" ></s:textfield>
<s:textfield name="flag" label="True/False?"></s:textfield>
<s:textfield name="age" label="Current Year?"></s:textfield>
<s:textfield name="date" label="Todays Date (mm/dd/yyyy)?"></s:textfield>
</table>
<h3>Struts 2 Array OGNL Example</h3>

<strong>Array with same name example</strong><br>
<table>
<s:textfield name="stocks" label="Stock 1"></s:textfield>
<s:textfield name="stocks" label="Stock 2"></s:textfield>
<s:textfield name="stocks" label="Stock 3"></s:textfield>
</table>

<strong>Array with indexed name example</strong><br>
<table>
<s:textfield name="roles[0]" label="Role 1"></s:textfield>
<s:textfield name="roles[1]" label="Role 2"></s:textfield>
<s:textfield name="roles[2]" label="Role 3"></s:textfield>
</table>

<h3>Struts 2 List OGNL Example</h3>

<strong>List with same name example</strong><br>
<table>
<s:textfield name="usersList.name" label="User 1 Name"></s:textfield>
<s:textfield name="usersList.name" label="User 2 Name"></s:textfield>
<s:textfield name="usersList.name" label="User 3 Name"></s:textfield>
</table>
<strong>List with indexed name example</strong><br>
<table>
<s:textfield name="fruitsList[0].name" label="Fruit 1"></s:textfield>
<s:textfield name="fruitsList[1].name" label="Fruit 2"></s:textfield>
<s:textfield name="fruitsList[2].name" label="Fruit 3"></s:textfield>
</table>

<strong>Map Example</strong>
<table>
<s:textfield name="usersMap['first'].name" label="User 1"></s:textfield>
<s:textfield name="usersMap['second'].name" label="User 2"></s:textfield>
<s:textfield name="usersMap['third'].name" label="User 3"></s:textfield>
</table>

<strong>Custom Converter Example</strong>
<table>
<s:textfield name="rectangle" label="Rectangle in format R:x,y"></s:textfield>
</table>
<s:submit label="Submit" align="left"></s:submit>
</s:form>
</body>
</html>

home.jsp is used as input page where user can provide values and invoke welcome action.

welcome.jsp


<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Welcome Page Example</title>
</head>
<body>
<h3>Struts 2 OGNL Examples</h3>
Name = <s:property value="name"></s:property><br>
True/False? = <s:property value="flag" ></s:property><br>
Current Year? = <s:property value="age"></s:property><br>
Todays Date (mm/dd/yyyy)? = <s:date name="date" format="MM/dd/yyyy"></s:date><br><br>

Stocks Array = <s:property value="stocks"/><br>
Roles Array = <s:property value="roles"/><br><br>
Users List = <s:iterator value="usersList"><s:property value="name"/>  </s:iterator><br>
Fruits List = <s:iterator value="fruitsList"><s:property value="name"/>  </s:iterator><br>
Fruit 1 Name = <s:property value="fruitsList[0].name"/><br><br>

Users Map = <s:iterator value="usersMap">{<s:property value="key"/>,<s:property value="value.name"/>}  </s:iterator><br><br>

Rectangle Dimensions: x = <s:property value="rectangle.x"/> and y = <s:property value="rectangle.y"/><br>
</body>
</html>

welcome.jsp is just used to show the values used by the user as a proof that OGNL is taking care of data transfer as well as type conversion.

Before we move on to discuss on custom converter classes, let’s look at some important points in above implementation.

  1. Basic data types conversion is automatic, we don’t need to follow any special rules for them.
  2. Struts 2 takes care of converting String to Date also and we can use s:date to display it with specific format.
  3. Arrays and Lists can be used with name as well as index. If we use index, then we need to initialize the array in the bean. That’s why roles array is initialized in the bean class.
  4. Do not initialize the list variables in bean else it will throw error. OGNL takes care of initialization and populating values.
  5. OGNL in Struts2 also provides built-in support for Map that we can use in result pages.
  6. We can use iterator with multi-values data types such as List, Map, Array to traverse through them. We can use index or key to get specific values from these variables.

Struts2 OGNL Custom Type Converter

Creating and configuring custom type converter class is very easy. First step is to fix the input format for the custom class. For my example, I have fixed the user input to be R:x,y where x and y are rectangle variables and should be integers.

Second step is to implement the converter class. Type converter classes should implement com.opensymphony.xwork2.conversion.TypeConverter interface.

Since in web application, we always get the request in form of String and send response in the form of String, Struts 2 API provides a default implementation of TypeConverter interface, StrutsTypeConverter.

StrutsTypeConverter contains two abstract methods – convertFromString to convert String to Object and convertToString to convert Object to String. We will extend this class for custom type converter.


package com.journaldev.struts2.typeconverters;

import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;

import com.journaldev.struts2.model.Rectangle;
import com.opensymphony.xwork2.conversion.TypeConversionException;

/**
 * Custom type converter to convert user input to Rectangle
 * Format is R:x,y where x and y are int defining Rectangle dimensions 
 * @author pankaj
 *
 */
public class RectangleTypeConverter extends StrutsTypeConverter {

	@Override
	public Object convertFromString(Map arg0, String[] inputs, Class arg2) {
		String input = inputs[0];
		if(!input.startsWith("R:")) throw new TypeConversionException("invalid input");
		input = input.substring(2);
		String[] dimensions = input.split(",");
		int x = Integer.parseInt(dimensions[0]);
		int y = Integer.parseInt(dimensions[1]);
		Rectangle rect = new Rectangle();
		rect.setX(x);
		rect.setY(y);
		return rect;
	}

	@Override
	public String convertToString(Map arg0, Object obj) {
		Rectangle rect = (Rectangle) obj;
		String output = "R:" + rect.getX() + "," + rect.getY();
		return output;
	}

}

Notice that code is very simple and parse input string to object and vice versa.

Next step is to configure the type converter to be used for Rectangle type variables. There are two ways to configure this – first is to configure for specific action and second way is to configure globally.

For action specific converter, we can use com.opensymphony.xwork2.conversion.annotations.TypeConversion annotation and change the setter method like below.


@TypeConversion(converter="com.journaldev.struts2.typeconverters.RectangleTypeConverter")
public void setRectangle(Rectangle rectangle) {
	this.rectangle = rectangle;
}

Custom Type Converter for ModelDriven Action Classes

If Action class is implementing ModelDriven interface for java bean, another way is to create property file with name as {JavaBeanName}-conversion.properties and put it in the same package as java bean class, so we can create MyJavaBean-conversion.properties and put it in com.journaldev.struts2.model package with below data.

MyJavaBean-conversion.properties


#For Action Classes implementing ModelDriven<MyJavaBean>
#variable-name=TypeConverter class name
rectangle=com.journaldev.struts2.typeconverters.RectangleTypeConverter

For global conversion, as I have done in this project, we need to create xwork-conversion.properties properties file and make sure it’s in WEB-INF/classes directory. We need to provide the class name and converter as key-value pair. For us it’s

xwork-conversion.properties


#Application level custom converter configuration
com.journaldev.struts2.model.Rectangle=com.journaldev.struts2.typeconverters.RectangleTypeConverter

Now when we will run our application, we will get following response pages.
Struts2 OGNL Custom Type Converter Input

Struts2 OGNL Custom Type Converter Response

That’s all for Struts2 OGNL example tutorial, I hope you liked it. Download project from below link and run yourself.

Comments

  1. hamed says:

    hi Pankaj, i am using Struts2 in Liferay v6.2 to create a portlet and there is a problem to submitting the form. after i submit the form all objects and fields in action class are null in other word no data passing to the action class.

    what should i do? how can fix this problem?

    data comes from action to view.jsp but not passing to action class after submit form.

  2. Ramkumar says:

    Hi pankaj,

    Simple & best way. Thanks yar.

    And i have an issue regarding the struts date conversion.

    while send a date from page to the action class its shows an error.

    I displayed it below,

    Unexpected Exception caught setting ‘clubForm.formDate’ on ‘class com.afl.lm.action.FormAction: Error setting expression ‘clubForm.formDate’ with value ‘[Ljava.lang.String;@774d32ae’
    Error setting expression ‘clubForm.formDate’ with value ‘[Ljava.lang.String;@774d32ae’ – Class: ognl.OgnlRuntime
    File: OgnlRuntime.java
    Method: callAppropriateMethod
    Line: 1305 – ognl/OgnlRuntime.java:1305:-1

    But this issue not reproduced in all systems. Can you help me out from this..

  3. Mohammed Sarfaraz says:

    Hey Pankaj,

    I like your tutorials.They are simple and elegant.

    Thank yaar for the wonderful effort you have put forward.

    BR
    Sarfaraz

  4. Rajeev says:

    Can we use custom type convert for only one specific field?

  5. John says:

    I donwload your project from this website.I want to ask a question.When the Tomcat server started,it has problem:Conversion registration error
    java.lang.ClassNotFoundException: com.journaldev.struts2.typeconverters.RectangleTypeConverter
    Also ,I want to write a WelcomeAction-conversion.properties instead of Annotation.So how to write it .
    Thank you.

    1. Pankaj says:

      Can you check if RectangleTypeConverter class is present in right package. You should be getting compile error if you are importing the project in Eclipse IDE.

      There is some issue with type converter for action classes where ModelDriven interface is implemented.
      Check out the question raised in SO by me:
      http://stackoverflow.com/questions/18993091/struts-2-type-converter-issue

      Till now I haven’t got any solution for this.

        1. Pankaj says:

          Got the answer that it needs to do in other way. we need to create java bean conversion file. Updated the post with details and downloadable project is also updated.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages