Thursday, August 16, 2012

Struts2 - Write your own Interceptor for Session checking

Interceptor Model is another great feature of Struts2.

Interceptors are similar to Servlet Filters (actually, both of them are based on intercepting filter),but more configurable and powerful (note that they need to be Thread-Safe).

SHORT STORY
Basically, every request to an Action is filtered through an Interceptor Stack, a specific list of Interceptors for that specific Action.
Any Interceptor performs some simple operations, then decides if the request can proceeds down the stack, to the next Interceptor (or to the Action, if we're on the last Interceptor), or it has to be redirected (back, or) somewhere else.
When not specified, a request passes through the DEFAULT INTERCEPTOR STACK, that covers most of the needs we may have.
But when our page has specific requirements, we can use a CUSTOM INTERCEPTOR STACK, made by a different number and kind of Interceptors (either available from Struts2 or, if the Interceptor with the logic that we want doesn't exist, created by ourselves).
As Apache Wiki says, "Each and every Interceptor is pluggable, so you can decide exactly which features an Action needs to support."
We can make all the custom stacks we want.
Here is the list of the Interceptors https://cwiki.apache.org/WW/interceptors.html#Interceptors-TheDefaultConfiguration
available on Struts2, and we can notice the Default Stack too, containing the majority of them.


SCENARIO
We want to perform a "Valid Session Checking" on our Actions, without writing any code into themselves or their configuration in Struts.xml.
The goal is NOT to handle user authentication (totally another pair of shoes), but to ensure that our Session is "certified" (think about an Web Application loading data into Session at the beginning, and expecting to find that data in the Session later, during its whole life cycle...).


WE NEED...
one "entry-point" Action (setting up a "mock" Session Validity Token), under the control of Default Interceptor Stack;
one Custom "SessionChecker" Interceptor;
one Custom Interceptor Stack (containing all the default interceptors PLUS our sessionCheckerInterceptor);
one Global Result "sessionExpired" on Struts.xml, redirecting to sessionExpired.jsp.


LET'S START
All of our Actions will extend a base Action called BaseAction:

package com.andrealigios.struts2validation.presentation.action;

    public abstract class BaseAction extends ActionSupport implements SessionAware, ServletResponseAware, ServletRequestAware{

        private static final long serialVersionUID = 1L; 
        public final static String SESSION_VALIDATION_TOKEN_KEY = "SESSION_VALIDATION_TOKEN_KEY";
        public final static boolean SESSION_VALIDATION_TOKEN_VALUE = true;

        private HttpServletRequest req;    

    
        /*  bla 
            bla 
            yada 
            yada */
        public HttpServletRequest getServletRequest() {
            return req;
        }

    }


We need an entry-point Action (let's call it SessionSetupAction), to put a token in session; we will try to read it later from our Interceptor,
to check if session is valid (if session is expired and replaced by a new one, our token won't be there);
In the example, the token is a boolean; in real world applications, obviously we will use something different (generated, unique, salted, obfuscated, crypted stuff...), to prevent attacks and, for example, avoid a user to access the same session of a previous user on the same browser if not every windows/tabs were closed between the logout of the first user and the login of the second one (and the logout procedure didn't destroy properly the session).

The SessionSetupAction must use the Default Interceptors Stack, because when we run this Action, we haven't put the token in the session yet, so we must bypass the sessionCheckerInterecptor this time.

    package com.andrealigios.struts2validation.presentation.action;    

    public class SessionSetupAction extends BaseAction {

        private static final long serialVersionUID = 1L;

         public String execute() throws Exception {
            try {
                getServletRequest().getSession().setAttribute(SESSION_VALIDATION_TOKEN_KEY, true);
            } catch (Exception e){
                // Mock exception log...
                e.printStackTrace();
            }

            return SUCCESS;
        }

    }



   
   
Let's write the first (and only, in our example) of "n" business Actions for our Web Application. Let's call it FirstRealPageAction:
   
    package com.andrealigios.struts2validation.presentation.action;    

    public class FirstRealPageAction extends BaseAction {

        private static final long serialVersionUID = 1L;

        public String execute() throws Exception {

            try {
                // Do business
                // Do business
                // Do business              

                return SUCCESS;
            } catch (Exception e){
                // Mock exception log...
                e.printStackTrace();
                return ERROR;
            }

        }

    }    




   
Now it's the turn of the JSPs:

    <!-- firstRealPage.jsp -->

    <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
        <head>
            <title>First Real Page</title>
        </head>

        <body>
            <p>
                This is one of the Web Application pages.  <br/>

                You are here, then the token is correctly loaded into the Session.
            </p>
        </body>
    </html>



    


    <!-- error.jsp              -->

    <%@ page isErrorPage="true" %>
    <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
        <head>
            <title>Error</title>
        </head>

        <body>
            <p>
                An error occurred!
            </p>
        </body>
    </html>





        

    <!-- sessionExpired.jsp       -->

    <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
        <head>
            <title>Session Expired</title>
        </head>

        <body>
            <p>
                This is the Session Expired page.<br/>

                You are here because: <br/>                

                1) Your session was properly set up, but is expired; <br/>                

                2) Your session was NOT properly set up (for example, you tried to reach this page directly by URL). <br/>
                
                Press the button to pass through the sessionSetup Action and configure your Session properly.
            </p>

            <s:submit action="sessionSetup" value="Restart from the Entry point" />

        </body>
    </html>

    





   
At this point, we only miss the configuration and the interceptor itself. The configuration, quite long but (i hope) well-commented, is the following:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 
"http://struts.apache.org/dtds/struts-2.0.dtd">

    <struts>        

        <package name="default-package" extends="struts-default">

            <interceptors>

                <!-- ========================================================== -->
                <!-- DECLARATION OF THE DEFAULT INTERCEPTOR STACK               -->
                <!-- ========================================================== -->                        

                <interceptor-stack name="defaultStack">
                    <interceptor-ref name="exception"/>
                    <interceptor-ref name="alias"/>
                    <interceptor-ref name="servletConfig"/>
                    <interceptor-ref name="i18n"/>
                    <interceptor-ref name="prepare"/>
                    <interceptor-ref name="chain"/>
                    <interceptor-ref name="scopedModelDriven"/>
                    <interceptor-ref name="modelDriven"/>
                    <interceptor-ref name="fileUpload"/>
                    <interceptor-ref name="checkbox"/>
                    <interceptor-ref name="multiselect"/>
                    <interceptor-ref name="staticParams"/>
                    <interceptor-ref name="actionMappingParams"/>
                    <interceptor-ref name="params">
                        <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
                    </interceptor-ref>
                    <interceptor-ref name="conversionError"/>
                    <interceptor-ref name="validation">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                    <interceptor-ref name="workflow">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                    <interceptor-ref name="debugging"/>
                </interceptor-stack>

            </interceptors>



            <!-- ========================================================== -->
            <!-- SIMPLE REDIRECT TO ENTRY POINT WHEN NO PAGE SPECIFIED      -->    
            <!-- ========================================================== -->

            <action name="welcome">    
                <result type="redirect-action">
                    <param name="actionName">sessionSetup</param>
                </result>  
            </action>



            <!-- ========================================================== -->
            <!-- ENTRY POINT ACTION, UNDER DEFAULT INTERCEPTOR STACK        -->
            <!-- ========================================================== -->        

            <action name="sessionSetup" class="com.andrealigios.struts2validation.presentation.action.SessionSetupAction">    
                <result type="redirect-action">
                    <param name="actionName">firstRealPage</param>
                </result>  
            </action>
            
        </package>



        <package name="our-custom-package" extends="struts-default">

            <interceptors>

                <!-- ========================================================== -->
                <!-- DECLARATION OF OUR CUSTOM INTERCEPTOR FOR SESSION CHECKING -->
                <!-- ========================================================== -->
                <interceptor name="our-mock-session-checker-interceptor" 
                            class="com.andrealigios.struts2validation.presentation.interceptor.MockSessionCheckerInterceptor"/>
                <!-- ========================================================== -->
 
                <!-- ============================================== -->
                <!-- DECLARATION OF OUR CUSTOM INTERCEPTOR STACK,   -->
                <!-- THAT WILL USE OUR CUSTOM INTERCEPTOR           -->
                <!-- ============================================== -->            
                <interceptor-stack name="ourCustomStack">

                    <interceptor-ref name="exception"/>
                    <!-- ===================================== -->
                    <!-- WE PUT OUR INTERCEPTOR REFERENCE HERE -->
                    <!-- ===================================== -->
                    <interceptor-ref name="our-mock-session-checker-interceptor"/>
                    <!-- ===================================== -->        
                    <interceptor-ref name="alias"/>
                    <interceptor-ref name="servletConfig"/>
                    <interceptor-ref name="i18n"/>
                    <interceptor-ref name="prepare"/>
                    <interceptor-ref name="chain"/>
                    <interceptor-ref name="scopedModelDriven"/>
                    <interceptor-ref name="modelDriven"/>
                    <interceptor-ref name="fileUpload"/>
                    <interceptor-ref name="checkbox"/>
                    <interceptor-ref name="multiselect"/>
                    <interceptor-ref name="staticParams"/>
                    <interceptor-ref name="actionMappingParams"/>
                    <interceptor-ref name="params">
                        <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
                    </interceptor-ref>
                    <interceptor-ref name="conversionError"/>
                    <interceptor-ref name="validation">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                    <interceptor-ref name="workflow">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                    <interceptor-ref name="debugging"/>
                </interceptor-stack>            
                <!-- ============================================== -->            
 
             </interceptors>



            <!-- ===================================================== -->
            <!-- THE STACK WE WILL USE FOR THE ACTIONS OF THIS PACKAGE -->
            <!-- ===================================================== -->
            <default-interceptor-ref name="ourCustomStack" />
            <!-- ===================================================== -->


            <global-results>
                <!-- ===================================================== -->
                <!-- THE GLOBAL RESULT USED TO REDIRECT A REQUEST TO THE   -->
                <!-- "SESSION EXPIRED" PAGE FROM WITHIN OUR INTERCEPTOR    -->
                <!-- ===================================================== -->
                <result name="sessionExpired">jsp/sessionExpired.jsp</result>
                <!-- ===================================================== -->
            </global-results>



            <action name="firstRealPage" class="com.andrealigios.struts2validation.presentation.action.FirstRealPageAction">
                <result name="success">jsp/firstRealPage.jsp</param>            
                <result name="error">jsp/error.jsp</param>
            </action>

        </package>

    </struts>




Got it ? When the Actions of the package "our-custom-package" get called, the request passes into our Interceptor, that will perform the check on the token.
Note that it's possible to use different stacks into one package, instead of different packages.

Final part, the one that brought you here: our custom Interceptor.


package com.andrealigios.struts2validation.presentation.interceptor;


import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.StrutsStatics;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
 

public class MockSessionCheckerInterceptor extends AbstractInterceptor implements Constants{



    private static final long serialVersionUID = 1L;    

    public String intercept(ActionInvocation invocation) throws Exception {

        final ActionContext context = invocation.getInvocationContext();

        try {                        

                HttpServletRequest request = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);                                

                String sessionToken = (String) request.getSession().getAttribute(SESSION_VALIDATION_TOKEN_KEY);                

                if (sessionToken==null || !sessionToken.equals(SESSION_VALIDATION_TOKEN_VALUE))

                    return "sessionExpired";

                    /*     If the session Token is not there (or is different from what we want), 
                            uses the Global Result "sessionExpired" to redirect the request
                            to a page that will notify the user, giving him the opportunity to 
                            restart    the Session in the right way.
                    */

                 /* Else, everything is fine, let's proceed down the stack ! */

                return invocation.invoke();

        } catch (Exception e){
            e.printStackTrace();
            return ERROR; // not defined in our example...
        } 

    }





   
The coding is over.
If you set up your simple Web Application like this, and try to access directly your "firstRealPage", you should be redirected to the sessionExpired page; the same thing should happen if you stay too much in the "firstRealPage" page and try to reload it...

Despite the messy exposition, I hope you enjoyed this ;)

Tuesday, June 12, 2012

JMS Queue in a Clustered Environment

Using JMS queues is quite simple once set up correctly.

But the simpleness immediately disappears when working in a clustered environment, whichever technology you are using; often you'll get NameNotFoundException or JMSException: Connection not found while trying to contact the JMS server.

The reasons can be many, but most of the time the problem is in the JNDI Lookup procedure: in a clustered environment, is important to preserve uniqueness of server names, to avoid the needs of complex tricks and the appearance of weird and mysterious errors.

A Technote from IBM explains that, in a Websphere clustered environment
"...in which 2 nodes have Application Servers with the same name, an attempt to lookup an EJB in the name space during an EJB invocation finds the wrong server.
[...]
The solution for this problem is to have different server names for the servers for 2 different nodes."

This happens because both Application Servers on different nodes have same name (server1 in the example), and trying to lookup server1 on ejb node from application node will instead return the server1 of application node, the local one.

The same thing happens on Weblogic, as they states in JMS Admin documentation (page 55):

"Note: JMS clients depend on unique WebLogic Server names to successfully access a cluster — even when WebLogic Servers reside in different domains. Therefore, make sure that all WebLogic Servers that JMS clients contact have unique server names."

Different server names, correct Lookups. That's all folks.

Hope that helps ;)

Monday, June 4, 2012

Struts2 XML VALIDATION

Struts 2 framework allows powerful and easy validation through Java, XML or Annotations.
The first way (override of Validate() method) should be avoided.
Both XML and Annotations are good ways to go, but as usual they're poorly documented / exampled.
Let's take a look at XML Validation:


STANDARD XML VALIDATION

Scenario:
You have a JSP that posts a form with new data (let's say one record about a person) to be inserted, to an Action named InsertOneRowAction.java.
Data posted are: Name and Birthday (KISS paradigm ftw...).
Fields in JSP page are named person.name and person.birthday (getters of Person.java value object).
Both fields are mandatory, and Birthday must be a valid date between 1990 and 2000.

Solution:
Create a file with the name of the action and append "-Validation.xml" at the end, in the same package of the action.
We will see then:
com.andrealigios.struts2validation.presentation.action.InsertOneRowAction.java
com.andrealigios.struts2validation.presentation.action.InsertOneRowAction-Validation.xml


Open XML validation file and write the desired rules in there, in this case:

  <field name="person.name">
      <field-validator type="requiredstring">
          <message><![CDATA[ Name is mandatory ]]></message>
      </field-validator>
  </field>
  <field name="person.birthday">
        <field-validator type="required">
            <message><![CDATA[ Birthday is mandatory ]]></message>
        </field-validator>
        <field-validator type="date">
            <param name="min">01/01/1990</param>
            <param name="max">01/01/2000</param>
            <message><![CDATA[ Birthday must be a valid date between ${min} and ${max} ]]></message>
        </field-validator>
    </field>



Now put the tag <fielderrors/> into your JSP.
Done!

Every time your data posted won't match the validation criteria, the Validation Interceptor will redirect your post to the source JSP page, showing the error messages and preventing wrong / incomplete data to reach the action's setters.


DYNAMIC XML VALIDATION - FIELDEXPRESSION VALIDATOR

Let's dig a bit deeper: we need min and max Date parameters to be dynamic, for example read from a database or a configuration file.
We need to create a getter for each parameter, that does its business inside, and returns the desired java.util.Date.
We can put the getter into the Action, or into Actions that are extended by ours (let's say BaseAction). Remind to preserve getters's visibility ;)

 
public Date getMinDateDynamicallyRead(){
 // Do business
 return minDate; 
}

public Date getMaxDateDynamicallyRead(){
 // Do business
 return maxDate; 
}


Now, let's modify XML Validation file as follows:

  <field name="person.birthday">
        <field-validator type="required">
            <message><![CDATA[ Birthday is mandatory ]]></message>
        </field-validator>
        <field-validator type="date">
            <message><![CDATA[ Birthday must be a valid date ]]></message>
        </field-validator>
      <field-validator type="fieldexpression">
        <param name="expression"> 
          <![CDATA[ person.birthday==null || (person.birthday >= minDateDynamicallyRead && person.birthday <=  maxDateDynamicallyRead) ]]>
        </param> 
        <message><![CDATA[ Birthday must be between ${minDateDynamicallyRead} and ${maxDateDynamicallyRead} ]]></message>
         </field-validator>
    </field>



The person.birthday == null part is necessary to avoid redundancy of messages. If date is not provided, then the "required" validator must raise the error message, but there's no need to show that date is not inside our desired interval too...

Note that you can chain multiple "fieldexpression" validators for a single field, performing different controls and showing different messages.


MULTIPLE ROWS XML VALIDATION - VISITOR VALIDATOR

Now let's understand the most powerful, less documented feature of Struts 2 Validation : VISITOR validation.

Scenario:
We are on a different JSP page, that shows us a list of persons, and allows us to perform multiple changes to their Names and Birthdays, and to save 'em all with one click on the UPDATE button.
The main object into the JSP is PersonsTable.java (exposed through a getter from the Action), that exposes a list of Person.java objects, cycled within an iterator in the JSP:


public List<Person> getPersonsFromDatabase(){ 
    return savedPersons; 
}

The JSP UPDATE button posts to an Action named UpdateMultipleRowsAction.java
(We obviously can't validate the persons in the previous way, because we can't (and definitely don't want to) write a huge, static XML file specifying row indexes ;)

Struts2 allows us to resolve the problem like follows:

Create the usual XML Validation file in action's package, beside the action:
com.andrealigios.struts2validation.presentation.action.UpdateMultipleRowsAction.java
com.andrealigios.struts2validation.presentation.action.UpdateMultipleRowsAction-Validation.xml

inside the xml file, let's write:

<validators>
  <field name="personsTable.personsFromDatabase">
      <field-validator type="visitor">
          <message></message>
      </field-validator>
  </field>
</validators>


This command will tell Struts to perform, for EVERY single row, the validation using the validation rules of the object listed (in this case, Person.java).
The real validation must be written into an xml file UNDER THE OBJECT PACKAGE (NOT the Actions package), with a name composed by the Object class concatenated to "-Validation.xml".

For example:
com.andrealigios.struts2validation.valueobject.Person.java
com.andrealigios.struts2validation.valueobject.Person-Validation.xml

Inside Person-Validation.xml, we can put the same rules used in previous examples, pointing to the name of the field (name of the getters of Person.java, in this case "name" or "birthday", without "person" or other stuff before), as follows:


  <field name="name">
      <field-validator type="requiredstring">
          <message><![CDATA[ Name is mandatory ]]></message>
      </field-validator>
  </field>
  <field name="birthday">
        <field-validator type="required">
            <message><![CDATA[ Birthday is mandatory ]]></message>
        </field-validator>
        <field-validator type="date">
            <param name="min">01/01/1990</param>
            <param name="max">01/01/2000</param>
            <message><![CDATA[ Birthday must be a valid date between ${min} and ${max} ]]></message>
        </field-validator>
    </field>


Enjoy, forgive my Engrish and... comment!! ;)