Monday, 18 January 2016

Struts2: Token interceptor

Some times user may submit the request twice, (or) refresh the page, in those conditions we shouldn’t let the application behave in strange way. By using Token interceptor, we can get rid of these double click kinds of problems. Whenever struts2 find double submission, token interceptor returns the result invalid.token, which can be mapped in your action configuration.

How to set a token in your form?
Use the tag <s:token/> in your form. This tag is required and must be used in the forms that submit to actions protected by this interceptor. Any request that does not provide a token (using the token tag) will be processed as a request with an invalid token.

In brief, following are the action, jsp files I am going to use.

File
Description
EntryAction.java
Action class that process the form.
index.jsp
Simple HTML form
invalid_token.jsp
Request redirect to this page on double submissions
success.jsp
You will get the page, after successful submission of the form (input.jsp).

Following step-by-step procedure explains simple application that uses token interceptor.

Step 1: Create new dynamic web project ‘struts2_token’ in Eclipse.

File -> New -> Dynamic Web Project.

Step 2: Mavenize the project. Right click the project -> Properties -> Configure -> Convert To Maven Project.


Open pom.xml, update struts2 maven dependencies. I am going to use following maven dependency.

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

Step 3: Define index.jsp like below. 

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>

<%@ taglib prefix="s" uri="/struts-tags"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<title>Insert title here</title>
</head>

<body>
  <s:form action="addEntry">
    <s:textfield label="Name" name="name" />
    <s:textfield label="Address" name="address" />
    <s:submit value="ADD" />
    <s:token />
  </s:form>
</body>

</html>


Step 4: Define success.jsp like below.

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <h1>Your details are submitted successfully</h1>
</body>
</html>


Define invalid_token.jsp like below. 
invalid_token.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>Error Page</title>
</head>
<body>
  <h3>User information is not updated, duplicate request detected.</h3>
  <h4>Possible Reasons are:</h4>
  <ul>
    <li>Back button usage to submit form again</li>
    <li>Double click on Submit button</li>
    <li>Using "Reload" Option in browser</li>
  </ul>
  <br>
  <s:if test="hasActionErrors()">
    <s:actionerror />
  </s:if>
</body>
</html>


Step 5: Define package com.sample, define the class EntryAction.java like below.

package com.sample;

import com.opensymphony.xwork2.ActionSupport;

public class EntryAction extends ActionSupport{
  public String execute(){
    return "success";
  }
}


Step 6: It is time to create struts.xml. Since Struts 2 requires struts.xml to be present in classes folder. Create struts.xml file under the WebContent/WEB-INF/classes folder. Eclipse does not create the "classes" folder by default, so you need to do this yourself. To do this, right click on the WEB-INF folder in the project explorer and select New > Folder. Create struts.xml file inside classes.

struts.xml

<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
  <package name="default" extends="struts-default">
    <action name="addEntry" class="com.sample.EntryAction">
      <interceptor-ref name="token" />
      <interceptor-ref name="defaultStack"></interceptor-ref>
      <result name="success">/success.jsp</result>
      <result name="invalid.token">/invalid_token.jsp</result>
    </action>
  </package>
</struts>


Step 7: Update web.xml like below. 
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>struts2_token</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <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>


Following is the complete project structure.
Run the application on server, you will get following page.


Fill the form and submit the data, you will get success page.
Refresh the success page, you will redirect to invalid_token.jsp.


Note:
While developing above application, I stuck with below error.


java.lang.NullPointerException
         at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:638)
         at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:614)
         at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:210)
         at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:139)
         at org.apache.struts2.interceptor.TokenInterceptor.getErrorMessage(TokenInterceptor.java:182)
         at org.apache.struts2.interceptor.TokenInterceptor.handleInvalidToken(TokenInterceptor.java:166)
         at org.apache.struts2.interceptor.TokenInterceptor.handleToken(TokenInterceptor.java:151)
         at org.apache.struts2.interceptor.TokenInterceptor.doIntercept(TokenInterceptor.java:142)
         at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
         at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
         at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
         at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564)
         at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
         at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
         at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
         at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
         at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
         at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
         at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
         at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
         at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
         at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
         at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
         at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
         at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
         at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
         at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
         at java.lang.Thread.run(Thread.java:745)

Solution for above error is, Your action class must extend ActionSupport.











Previous                                                 Next                                                 Home

1 comment:

  1. I am facing similar issue, i am getting null pointer exception even if i extend the Action class to ActionSupport class, Please help me with it

    ReplyDelete