Ajax validation in Struts 2

Problem

Check the availability of ‘Username’ during registration in J2EE web application using Struts 2 and Ajax.

Solution

1. Create the following JavaScript functions for Ajax implementation:

function GetXmlHttpObject()
{
    var xmlHttp=null;
    try
      {
          // Firefox, Opera 8.0+, Safari
          xmlHttp=new XMLHttpRequest();
      }
    catch (e)
      {
          // Internet Explorer
          try
        {
            xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
        }
          catch (e)
        {
            xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
        }
      }
    return xmlHttp;
}

function AJAXInteraction(url, callback) 
{
    var xmlHttp = GetXmlHttpObject();
    if (xmlHttp==null)
    {
          alert ("Your browser does not support AJAX!");
          return;
    }
    
    xmlHttp.onreadystatechange = processRequest;
        
    
    function processRequest () 
    {
      // readyState of 4 signifies request is complete
      if (xmlHttp.readyState == 4) 
      {
        // status of 200 signifies sucessful HTTP call
        if (xmlHttp.status == 200) 
        {
             if (callback) callback(xmlHttp.responseXML);
        }
      }
    }
    
    this.doGet = function() 
    {
      xmlHttp.open("GET", url, true);
      xmlHttp.send(null);
    }
}

function validateCallback(responseXML) 
{
    var msg = responseXML.getElementsByTagName("valid")[0].firstChild.nodeValue;
    if (msg == "false")
    {
        var mdiv = document.getElementById("userIdMessage");
        // set the style on the div to invalid
        document.getElementById("showErrLabel").style.display="block";                
        document.getElementById("showErr").style.display="block";                                
        mdiv.className = "contentformlabel_error";
        mdiv.innerHTML = "Username already exists.";
    } 
    else if(msg == "true")
    {
        var mdiv = document.getElementById("userIdMessage");
        // set the style on the div to valid
        document.getElementById("showErrLabel").style.display="none";                
        document.getElementById("showErr").style.display="none";                                
        mdiv.className = "contentformlabel";
        mdiv.innerHTML = "Username available";
    }
    else
    {
        var mdiv = document.getElementById("userIdMessage");
           // set the style on the div to valid
        document.getElementById("showErrLabel").style.display="block";                
        document.getElementById("showErr").style.display="block";                                
        mdiv.className = "contentformlabel_error";
        mdiv.innerHTML = "Please enter a username";
    }            
}

2. Create another JavaScript function which will be called by the input html element

function validateUserId() 
{
    // /marketer/validate.action = namespace/action
    var url = '<%=request.getContextPath()%>'+"/marketer/validate.action?userId="+document.getElementById("marketer.username_id").value;    
    var ajax = new AJAXInteraction(url, validateCallback);
    ajax.doGet();
    return false;
}

3. Create the HTML

<table>
    <tr>
        <td class="contentformlabel">
            <table cellpadding="0" cellspacing="0" align="right">
                <tr>
                    <td style="vertical-align: middle; padding-right: 6px">
                        <div id="showErrLabel" style="display:none;">
                          <img src="../images/alert_icon.gif">
                        </div>
                    </td>
                    <td style="white-space: nowrap">
                        <font class="contentformlabel_error">*</font> Username:
                    </td>
                </tr>
            </table>        
        </td>
        <td class="contentformfield">
            <s:textfield id="marketer.username_id" cssClass="contentforminput-smallhgt" name="marketer.username" size="40" maxlength="25"/>
        </td>
        <td colspan="2" style="vertical-align: middle">
            <table cellpadding="0" cellspacing="0" align="left">
                <tr>
                    <td style="vertical-align: middle; padding-top: 3px; padding-right: 3px">
                        <input type="image" src="../images/check_availability.gif" onclick="return validateUserId();">
                    </td>
                    <td style="vertical-align: middle; padding-top: 3px; padding-right: 3px">
                        <div id="showErr" style="display:none;" align="left">
                            <img src="../images/left_arrow_red.gif" align="left">                    
                        </div>
                    </td>
                    <td style="vertical-align: middle; padding-top: 3px; padding-right: 3px">
                        <div id="userIdMessage" align="left"></div>
                    </td>
                </tr>
            </table>    
        </td>                                                
    </tr>
</table>

4. Create the relevent stylesheets and call it in html

5. Create the Action which will validate the value of “username”

    Response object is abstracted in Struts 2 hence will not be able to access it directly  from action. So we need to create a custom result object and get the response from it.

package com.util;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.dispatcher.StrutsResultSupport;

import com.ajaxproj.action.MarketerAction;
import com.ajaxproj.domain.account.Marketer;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;

public class ValidateUsername extends StrutsResultSupport
{
    private static final Log log = LogFactory.getLog(ValidateUsername.class);
    
    @Override
    protected void doExecute(String arg0, ActionInvocation arg1) throws Exception 
    {
        HttpServletRequest request   = (HttpServletRequest) ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);        
        ActionContext context        = arg1.getInvocationContext();
        HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);

        HttpSession userSession   = request.getSession(true);
        Marketer formMarketer     = null;
        
        MarketerAction  action    = ((MarketerAction) arg1.getAction());
        formMarketer              = action.getMarketerService).checkDuplicateUsername(username);

        String str                = null;
        PrintWriter out           = response.getWriter();
        StringBuffer buffer       = new StringBuffer(255);
        
        // check duplicate username
        if((username != null) && formMarketer != null)
        {
            response.setContentType("text/xml");
            response.setHeader("Cache-Control", "no-cache");
            response.getWriter().write("<valid>false</valid>");
        }
        else if(username != null && formMarketer == null)
        {
            response.setContentType("text/xml");
            response.setHeader("Cache-Control", "no-cache");
            response.getWriter().write("<valid>true</valid>");            
        }
        else
        {
            response.setContentType("text/xml");
            response.setHeader("Cache-Control", "no-cache");
            response.getWriter().write("<valid>Empty</valid>");
        }
    }    
}

6.  Create the default Action

7. Edit Struts.xml (can be struts-<namespace>)

<!DOCTYPE struts PUBLIC 
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="marketer" namespace="/marketer" extends="struts-default">
    <result-types>
        <result-type name="validateUserName" class="com.util.ValidateUsername" />
    </result-types>

    <action name="validate" class="com.action.MarketerAction">
        <result type="validateUserName">/pages/userregistration/marketerUserInfo.jsp</result>
    </action>
</package>
</struts>