Custom Converter in Struts2

Hi friends,

Converters are much needed and useful tools while coding. Fortunately Struts2 supports several type converters. Listing few of them.

1) String
2) boolean / Boolean
3) char / Character
4) int / Integer, float / Float, long / Long, double / Double

5) dates – uses the SHORT format for the Locale associated with the current request
6) arrays – assuming the individual strings can be converted to the individual items
7) collections – if not object type can be determined, it is assumed to be a String and a new ArrayList is created.

We can get more tips from here. Also see the java documentation about TypeConverter interface here.

Thanks to Strtus2. But, lots of things equivalent to nothing. What will we do, if we are not satisfied with these built in converters, or what will we do if we want to work on our own data types? Yes, we need “indigenous converters”. Here I’m telling the work around for creating and implementing custom converters.

Converters can be created by

1) Extending org.apache.struts2.util.StrutsTypeConverter class and overriding its two abstract methods.
2) Implementing ognl.TypeConverter interface and overriding its one and only one method.

Though I am new to the conversion, I believe both StrutsTypeConverter class and TypeConverter interface do the same job.

Anyway let’s come to the point. It’s time to create our own converter.

Step 1: Create a JSP file, input.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Converter Example</title>
    </head>
    <body>
        <s:form action="convert">
            <s:textfield name="pojo.time" label="Enter time in 24 hour format (hh:mm)"/>
            <s:textfield name="pojo.amount" label="Enter amount in Dollars"/>
            <s:submit value="Convert"/>
        </s:form>
    </body>
</html>

Here I’m going to convert the time from 24 hour format to 12 hour format and amount from Dollars to Rupees.

Step 2: Create a POJO class, ConverterPojo.java

package com.eleventhHour.struts2;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
public class ConverterPojo {
    private String time, amount;
    public String getAmount() {
        return amount;
    }
    @TypeConversion(converter = "com.eleventhHour.struts2.CurrencyConverter")
    public void setAmount(String amount) {
        this.amount = amount;
    }
    public String getTime() {
        return time;
    }
    public void setTime(String time) {
        this.time = time;
    }
}

Here, we have to notice the annotation @TypeConversion(converter = “com.eleventhHour.struts2.CurrencyConverter”). This annotation specifies the location of one of my converters. literally, by using this annotation, I register my class com.eleventhHour.struts2.CurrencyConverter as a converter, and gets executed every time when setAmount(String amount) method is invoked.

Step 3: Create an action class, ConverterAction.java


package com.eleventhHour.struts2;

import com.opensymphony.xwork2.ActionSupport;


public class ConverterAction extends ActionSupport {

    private ConverterPojo pojo;

    public ConverterPojo getPojo() {
        return pojo;
    }
    public void setPojo(ConverterPojo pojo) {
        this.pojo = pojo;
    }
    @Override
    public String execute() {
        return SUCCESS;
    }
}

The action class contains nothing rather than the setter and getter of our ConverterPojo. An execute() method also there, which returns a SUCCESS of String type.

Before scrolling down, we have to know that “Everything moves on a HTTP request is treated as String by the protocol, i.e., numbers, booleans, dates etc. are treated as String by the protocol.”

Step 4: Create a Converter class, CurrecyConverter.java


package com.eleventhHour.struts2;

import com.opensymphony.xwork2.conversion.TypeConversionException;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;


public class CurrencyConverter extends StrutsTypeConverter {

    private static final double EXCHANGE_RATE = 55.11;
    private static final Locale LOCALE_INR = new Locale("en", "IN");
    private NumberFormat INR_FORMATTER = NumberFormat.getCurrencyInstance(LOCALE_INR);

    @Override
    public Object convertFromString(Map map, String[] strings, Class type) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String convertToString(Map map, Object obj) {
        try {
            String[] str = (String[]) obj;
            return INR_FORMATTER.format(EXCHANGE_RATE * Double.parseDouble(str[0]));
        } catch (Exception e) {
            throw new TypeConversionException(e);
        }
    }
}

Here, it can be seen that I extended my CurrencyConverter class from StrutsTypeConverter class and I overrode both of its abstract methods.

The method “public Object convertFromString(Map map, String[] strings, Class type)” executed when I convert a String type to any Object type and returns an object value. Here I’m not going to use this method.

The next method “public String convertToString(Map map, Object obj)” executed when I convert an Object to a String and which in turn returns that converted string value. Here, I use this method to convert my amount from Dollars to Rupees and format the amount to Indian locale.

Now, we have seen one way of registering converters. Another method of registration is the use of “properties” file, which will be discussed below.

Step 5: Create another Converter class, TimeConverter.java


package com.eleventhHour.struts2;

import java.lang.reflect.Member;
import java.text.SimpleDateFormat;
import java.util.Map;
import ognl.TypeConverter;

public class TimeConverter implements TypeConverter {

    private static final SimpleDateFormat INFORMAT = new SimpleDateFormat("HH:mm");
    private static final SimpleDateFormat OUTFORMAT = new SimpleDateFormat("hh:mm a z");

    @Override
    public Object convertValue(Map map, Object o, Member member, String string, Object obj, Class type) {
        try {
            String[] str = (String[]) obj;
            return OUTFORMAT.format(INFORMAT.parse(str[0]));
        } catch (Exception e) {
            throw new UnsupportedOperationException(e);
        }

    }
}

Instead of extending StrutsTypeConverter, this time I implement ognl.TypeConverter interface. The interface is having one and only one method, which have been overridden above. This time, registration process of my converter goes on another way.

Step 6: Create a property file, ConverterAction-conversion.properties

pojo.time = com.eleventhHour.struts2.TimeConverter

There’s a naming convention that, the property file name should be like [action-class]-conversion.properties and should be put in the same package where our action-class resides. This is the another way how I register my class as a converter.

Step 7: Create a JSP file, output.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Converter Example</title>
    </head>
    <body>
        Converted Time: <b><s:property value="pojo.time"/></b>
        <br/>
        Converted Amount <b><s:property value="pojo.amount"/></b>
    </body>
</html>
 

Step 8: Finally the mapping, struts.xml


<struts>
    <constant name="struts.devMode" value="true" />
    <package name="default" extends="struts-default">
        <action name="convert"
            class="com.eleventhHour.struts2.ConverterAction" method="execute">
            <result name="success">/output.jsp</result>
            <result name="input">/output.jsp</result>
        </action>
    </package>
</struts>

So, all the requisites for running our example are over. We just need to run the application. Go ahead. 🙂

input.jsp
input.jsp

output.jsp
output.jsp

Now, another question arises. What will we do if we want to declare a converter globally? Suppose, we have several packages and in each and every package, we have to use a Date object(for example) and need to convert a String value, entered by the user, to that Date object. As we have learned so far, we have to use either an annotation in every file, wherever the Date object is referenced, or use a property file in every package, both practices become a headache. So what we do is declaring a global converter for our entire application, which will be discussed below. Thanks again to Struts2 and xwork. We can easily do that. I am briefing it also here.

Step1: Create a JSP page, index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Converter Example</title>
    </head>
    <body>
        <s:form action="convert">
            <s:textfield name="pojo.uiDate" label="Date"/>
            <s:submit value="Press"/>
        </s:form>
    </body>
</html>

Step 2: Create a POJO class, ConverterPojo.java

package com.eleventhHour.struts2;


import java.util.Date;

public class ConverterPojo {

    private Date uiDate;

    public void setUiDate(Date uiDate) {
        this.uiDate = uiDate;
    }

    public Date getUiDate() {
        return uiDate;
    }
}

Here comes a difference from our previous POJO. The difference is that we created a Date object instead of a String object. Because our objective is to convert the date, entered by the user, from String type to java.util.Date type.

Step 3: Create an action class, ConverterAction.java

package com.eleventhHour.struts2;


import com.opensymphony.xwork2.ActionSupport;

public class ConverterAction extends ActionSupport {

    private ConverterPojo pojo;

    public ConverterPojo getPojo() {
        return pojo;
    }

    public void setPojo(ConverterPojo pojo) {
        this.pojo = pojo;
    }

    @Override
    public String execute() {
        return SUCCESS;
    }
}

No worry about it. It’s the same as our above action class.

Step 4: Create a converter class, DateConverter.java

package com.eleventhHour.struts2;

import com.opensymphony.xwork2.conversion.TypeConversionException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;

public class DateConverter extends StrutsTypeConverter {

    private static final DateFormat INPUT_FORMAT = new SimpleDateFormat("dd/MM/yyyy");
    private static final DateFormat OUTPUT_FORMAT = new SimpleDateFormat("E, MMM dd, yyyy");

    @Override
    public Object convertFromString(Map context, String[] values, Class clazz) {
        try {
            return INPUT_FORMAT.parse(values[0]);
        } catch (Exception e) {
            throw new TypeConversionException(e.getMessage());
        }
    }

    @Override
    public String convertToString(Map context, Object value) {
        try {
            return OUTPUT_FORMAT.format(value);
        } catch (Exception e) {
            throw new TypeConversionException(e.getMessage());
        }
    }
}

Here, the first method “public Object convertFromString(Map context, String[] values, Class clazz)” is called when the String value is converted to Date object. It happens when an HTML form is submitted. The second method “public String convertToString(Map context, Object value)” is called when a Date object is again converted to a String. It happens when the object gets printed on an HTML page.

Next we have to register this converter globally, for that we have to create a global property file.

Step 5: Create a property file, xwork-conversion.properties

java.util.Date=com.eleventhHour.struts2.DateConverter

From now on, all the Date objects of our application will be converted using our custom converter. There’s also a naming convention. The file name must be “xwork-conversion.properties” and should be put in our root package for the global view.

Step 6: Create a JSP file, result.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        Converted Date : <b><s:property value="pojo.uiDate"/></b>
    </body>
</html>

Step 7: Finally the mapping, struts.xml

<struts>
    <package name="default" extends="struts-default">
        <action name="convert" class="com.eleventhHour.struts2.ConverterAction" method="execute">
            <result name="success">/result.jsp</result>
            <result name="input">/result.jsp</result>
        </action>
    </package>
</struts>

index.jsp
index.jsp

result.jsp
result.jsp

Thanks.