Tuesday, April 3, 2007

Step By Step AJAX

As i said earlier about my project Applying Leave thru online. I used that opportunity to learn AJAX also.

Requirement for AJAX in that project is, there is a user called Admin who has the rights to do the admin operations like add, modify and delete. What i did is links will be showed based on the designation of the user after successful login. Admin is the person to enable what are all the links to be shown to this designation. If Admin decides to remove a particular link to the designation, he has select the link from the assigned links and submit.

Assume that in Link removal page all the designation is showed in a combo box. By selecting a particular designation, page has to show the assigned links to this designation without refreshing the page.

Here is the code in JSP
<html:form action="removeRoleToDesignation">
<table width="50%" align="CENTER">
<tr>
<td width="36%"> Select Designation </td>
<td width="100%">
<html:select property="designationId" onchange="showRole(this.value)">
<html:option value="NA"> --select--</html:option>
<html:options collection="ALLDESIGNATION" labelProperty="designation" property="id"/>
</html:select>
</tr>
</table>
<div id="roleToRemove"> </div>
<table width="50%" align="CENTER">
<tr>
<td colspan="2" align="center" >
<html:submit styleClass="button"/>
</td>
</tr>
</table>
</html:form>


Here if user change the value it call the function showRole with current value as argument.

Here is the code for ShowRole Javascript function


var xmlHttp;
function showRole(str){
if(str == 'NA'){
document.getElementById("roleToRemove").innerHTML="";
}else{
var url="roleInfo?&desigId="+str+"&amp;amp;amp;amp;amp;amp;amp;ranid="+Math.random();
xmlHttp=GetXmlHttpObject(roleStateChanged)
xmlHttp.open("GET", url , true)
xmlHttp.send(null)
}
}


Here if user selected the --Select-- in Combo box then no roles need to display. So innerHTML set as empty.

In else condition i.e if user selects any one of the designation we have to send the request to fetch the assigned roles for this desingation from DB.

roleInfo is the servlet Mapping in web.xml and desigId is the selected value in combo box.

diffId is the one to differanciate the reqeust by sending the random number. If you are not using this there is a chance of getting the cached information from the browser.

var url="roleInfo?&did="+str+"&amp;amp;amp;amp;amp;amp;amp;diffId="+Math.random();

Javascript communicates with the server thru XMLHttpReqeust object.

xmlHttp=GetXmlHttpObject(roleStateChanged)

Here the code to get the XMLHttpRequest for various browsers..

function GetXmlHttpObject(handler){
var objXmlHttp=null
if (navigator.userAgent.indexOf("Opera")>=0) {
alert("This example doesn't work in Opera")
return
}
if (navigator.userAgent.indexOf("MSIE")>=0){
var strName="Msxml2.XMLHTTP"
if (navigator.appVersion.indexOf("MSIE 5.5")>=0){
strName="Microsoft.XMLHTTP"
}
try {
objXmlHttp=new ActiveXObject(strName)
objXmlHttp.onreadystatechange=handler
return objXmlHttp
}
catch(e){
alert("Error. Scripting for ActiveX might be disabled")
return
}
}
if (navigator.userAgent.indexOf("Mozilla")>=0){
objXmlHttp=new XMLHttpRequest()
objXmlHttp.onload=handler
objXmlHttp.onerror=handler
return objXmlHttp
}
}

Getting XMLHttpRequest is depend on the browser. so i have to use the same code for getting the XMLHttpRequest. The onreadystatechange function will process the response from the server

objXmlHttp.onreadystatechange=handler

here i am passing the roleStateChanged function as the handler to this GetXmlHttpObject function

xmlHttp=GetXmlHttpObject(roleStateChanged)

Here is the code for roleStateChanged function

function roleStateChanged(){
if (xmlHttp.readyState==4 xmlHttp.readyState=="complete") {
var ajaxReturn = xmlHttp.responseText;
if( ajaxReturn == 'NOROLE'){
document.getElementById("roleToRemove").innerHTML="";
document.laForm.designationId.options[0].selected = true;
alert('No Roles to Remove');
}else{
document.getElementById("roleToRemove").innerHTML=ajaxReturn;
}
}
}


look at this code if

(xmlHttp.readyState==4 xmlHttp.readyState=="complete") {

XMLHttpRequest Object has the property readyState. It explains the status of the response of server.

The followings are the possible statevalues
0 = The request is not initialized
1 = The request has been set up
2 = The request has been sent
3 = The request is in process
4 = The request is complete


XMLHttpRequest object has responseText property thru which we can get the information from the response.
var ajaxReturn = xmlHttp.responseText;

Look at the following line in showRole function
xmlHttp.open("GET", url , true)

this open method of XMLHttpRequest has 3 argument. the first one is used to define GET or POST method. second one is the actual URL where action takes place. Third one states that whether this request is executed asynchronusly ( true ) or not ( false ).


Now we will see what happened in the back end ( Servlet )

I have delegated the request from servlet.


public void findRoleByDesignation( HttpServletRequest request, HttpServletResponse response ) throws LASException {

/* Here getting the information passed from request */

String designationId = request.getParameter( "desigId" );
HttpSession session = request.getSession();

/* DesignationVO is the view class which has the setter and getter for Designation properties like id, desc....*/

DesignationVO designationVO = LASControllerFactory.getInstance().getController().getAllRoleByDesignation( new Long( designationId ) );

try{
/* Here writing the Html code in response writer. we can get these information thru XMLHttpRequest.responseText */
PrintWriter out = response.getWriter();
if ( designationVO != null ){

out.println( "<table width=\"50%\" "align=\"CENTER\">" );
out.println( "<tr>" );
out.println( "<td class=\"tdbgrigio\" width=\"36%\"> Enter Designation </td>" );
out.println( "<td class=\"tdbgrigio\" width=\"100%\"> " );
out.println( "<select name=\"roles\" multiple size=12 width=\"100%\">" );

session.setAttribute( "DESIROLETOREMOVE", designationVO );
Collection roleList = new ArrayList();
Set role2DesignationSet = designationVO.getRole2Designation();
Iterator it = role2DesignationSet.iterator();

while ( it.hasNext() ){
Role2DesignationDTO role2DesignationDTO = ( Role2DesignationDTO ) it.next();
roleList.add( role2DesignationDTO.getRole() );

out.println( "<option value=" + role2DesignationDTO.getRole().getId() + ">" + role2DesignationDTO.getRole().getDescription() + "</option>" );

}
out.println( "</select>" );
out.println( "</td>" );
out.println( "</tr>" );
out.println( "</table>" );

}
else{

/* If this designation id has no role to show then it will pass the message NOROLE to responseText. This is just for identification */
session.removeAttribute( "DESIROLETOREMOVE" );
out.print("NOROLE");
}

out.close();
}
catch ( IOException ioException ){
throw new LASException( ILASErrorCode.TECHNICAL_PROPLEM );
}
}

in roleStateChanged function,

var ajaxReturn = xmlHttp.responseText;
if( ajaxReturn == 'NOROLE'){
document.getElementById("roleToRemove").innerHTML=""; document.laForm.designationId.options[0].selected = true;
alert('No Roles to Remove');
}else{
document.getElementById("roleToRemove").innerHTML=ajaxReturn;
}

If response from the server returns NOROLE then i showed a alert box with No Roles to Remove information, else a multiple select box will appear with assigned roles as option values.

Getting the XMLHttpRequest is always same. There are lots custom tld's available in internet. Try with them. Keep in mind that AJAX is not an new technology... It is not a tough to learn... Good Luck.....


kick it on DotNetKicks.com

Session Invalidation in struts

Some point of time i realize the error that if i keep the page idle for some time and i do any action in the page it will throw an error because of session invalidation.

Initially i thought to leave as it is and the user has to login again as i won't be get paid a single rupee for this. Later point of time, i thought it couldn't be a good thing to have a page like that. So i need to code to avoid such kind of error. Better way is redirect to login page or a common page where i can say "Your session is timed out ". i chose the first one that redirect to login page.

Then i googled to find out the solutions. But it is not easy to get the solutions. Finally i managed to get one solution.


First we should know how the request is processed in struts.

ActionServlet is the only servlet in Struts framework, and is responsible for handling all of the requests. Whenever it receives a request, it first tries to find a sub-application for the current request. Once a sub-application is found, it creates a RequestProcessor object for that sub-application and calls its process() method by passing it HttpServletRequest and HttpServletResponse objects.

This RequestProcessor class has so many methods to override. According to your business you can override the specific methods to acheive your goals. Now we have to concentrate on processPreProcess method for the above scenario.

Method Signature :
protected boolean processPreprocess javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)

If this method returns false then the request will abort the processing. we can create our own RequestProcessor and implementing those required methods.

Here is the code to use

package org.val.system.la.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.struts.action.RequestProcessor;


public class LASRequestProcessor extends RequestProcessor {
protected boolean processPreprocess( HttpServletRequest request, HttpServletResponse response ) {


boolean isValid = true;

HttpSession httpSession = request.getSession( false );

/* If the request comes from login or index then it should not block the process as these are the gateways in my project. In my project, the user has to login to proceed. Once the user has successful login then i put the information about him into session */

if ( request.getServletPath().equals( "/login.do" ) request.getServletPath().equals( "/index.do" ) ){
isValid = true;
}
else if ( httpSession != null && httpSession.getAttribute( "EMPLOYEEDETAILS" ) != null ){

/* Here we know that the user has successfully logged in as the session has his details */

isValid = true;
}else{
try{

/* Here you can easily assume that the session has expired or user has not yet logged in. One more case is copy the URL and paste it into another browser window. In these cases i forward the request to login page. You can do whatever you want */


request.getRequestDispatcher( "webapp/jsp/Index.jsp" ).forward( request, response );
isValid = false;
}
catch ( Exception ex ){
ex.printStackTrace();
}
}
return isValid;
}
}

Now we have created our custom class. But how does the Struts know that to use our RequestProcessor class.

Here is code to use

Put the below entry in struts-config.xml after action-mappings tag



<controller>
<set-property property="processorClass" value="org.val.system.la.action.LASRequestProcessor">
</controller>


This tag forced to use our RequestProcessor class.

Now if your session is timed out your application goes to login page......

Note: How to invalidate session thru web.xml


<session-config>
<session-timeout>15</session-timeout>
</session-config>


Here 15 is the minutes to keep the session alive. If the session is idle for 15 minutes then it will be timed out. You can set as 1 to test.

New to STRUTS and HIBERNATE

Last Month i got an opportunity thru my roommate Gopi to do a project. His requirement is to apply leave thru online. So i used this opportunity to learn Struts and Hibernate. He gave me a short time span ( April 1st 2007 is the deadline). So i started coding without reading any documents related to Struts and Hibernate as i have planned to read the documents whenever i get the doubts.

Here i would like to share what i have learned so far in struts and hibernate. Keep in mind that i am new to struts and hibernate. If you find anything wrong please let me know thru comments to update myself.