Struts2 execAndWait interceptor example for long running Actions

Sometimes we have long running actions where user will have to wait for final result. In this case, user can get annoyed with no response and may refresh the page causing issues in application.

Struts2 provide execAndWait interceptor that we can use for long running action classes to return an intermediate result to user while the processing is happening at server side. Once the processing is finished, user will be presented with the final result page.

Struts2 execAndWait interceptor is already defined in the struts-default package and we just need to configure it for our action classes. The implementation is present in ExecuteAndWaitInterceptor class that returns “wait” result page until the processing of action class is finished.

The interceptor provides two variables – delay to return the wait response for first time and delaySleepInterval to check if the action class processing is finished. Both of these variables can be overridden in struts configuration and measured in milliseconds.

We will look into this with a simple web application project whose project structure looks like below image. Create the dynamic web project in Eclipse with name as Struts2ExecAndWait and configure it as maven project.

Struts2-execAndWait-project

Project Configuration

Add below filter in web.xml to configure web application to use Struts2 framework.

	<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>

Add struts2-core dependency in pom.xml file like below.

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

struts.xml configuration

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
	<!-- constant to define result path locations to project root directory -->
	<constant name="struts.convention.result.path" value="/"></constant>
	
	<package name="user" namespace="/" extends="struts-default">
		<action name="run">
			<result>/run.jsp</result>
		</action>
		<action name="ExecuteTask" class="com.journaldev.struts2.actions.ExecuteTaskAction">
			<interceptor-ref name="defaultStack"></interceptor-ref>
			<interceptor-ref name="execAndWait">
				<!-- override delay and delaySleepInterval parameters of execAndWait to 500ms -->
				<param name="delay">500</param>
				<param name="delaySleepInterval">500</param>
			</interceptor-ref>
			<result name="wait">/running.jsp</result>
			<result name="success">/success.jsp</result>
		</action>
	</package>

</struts>

The entry point of application is run.action through which user will provide time to execute the task in seconds. The task will be executed by ExecuteTaskAction. We have overridden the interceptors for ExecuteTask action to have defaultStack first and then execAndWait interceptor.

Note that execAndWait interceptor should be the last one in the interceptors stack. We are also overriding delay and delaySleepInterval values to 500ms. Default value for delay is 0 and delaySleepInterval is 100ms.

ExecuteAndWaitInterceptor returns wait result as intermediate response, so we need to configure this as result page for the action. Once the processing is finished success result is returned to the user.

Action Class

package com.journaldev.struts2.actions;

import java.util.Date;

import com.opensymphony.xwork2.ActionSupport;

public class ExecuteTaskAction extends ActionSupport {

	@Override
	public String execute() {
		//process task for given time
		System.out.println("Starting execution. Current time: "+new Date());
		processTask();
		System.out.println("Ending execution. Current time: "+new Date());
		return SUCCESS;
	}

	private void processTask() {
		System.out.println("Time to process:"+processingTime);
		try {
			Thread.sleep(getProcessingTime()*1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private int processingTime;

	public int getProcessingTime() {
		return processingTime;
	}

	public void setProcessingTime(int processingTime) {
		this.processingTime = processingTime;
	}

	
}

Action class is very simple and just waits for input time to return the success response. In real life, there might be heavy processing involved causing the delay in response.

JSP Pages

<%@ 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>Long Process Input Page</title>
</head>
<body>
<h3>ExecAndWait Interceptor Example</h3>
<s:form action="ExecuteTask">
<s:textfield name="processingTime" label="Enter seconds to execute task"></s:textfield>
<s:token />
<s:submit name="submit" label="Run" align="center"></s:submit>
</s:form>
</body>
</html>

This is the entry point of the application and use in run action. We are required to have token in the form so that execAndWait interceptor can identify the request.

<%@ 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">
<meta http-equiv="refresh" content="2;url=<s:url includeParams="all" />"/>
<%--
<meta http-equiv="refresh" content="2"/>"/>
Above refresh meta will also work as long as browser supports cookie, 
by including params above we are making sure that it will work even when cookies are disabled
 --%>
<title>Processing intermediate page</title>
</head>
<body>
<h3>Your request is getting processed</h3>
<img alt="processing" src="images/processing.gif" align="middle">
</body>
</html>

This is the intermediate response returned by the execAndWait interceptor. This page should have “refresh” meta where request will be sent to server to get the final response. If we won’t have this, then browser will not send the further requests and stuck on this page. We are refreshing the page every 2 seconds and passing all the input parameters to server. We are also showing processing.gif in the page to look like some processing is happening.

<%@ 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>Long Process Success Page</title>
</head>
<body>
<h3>Task executed for <s:property value="processingTime"/> seconds successfully.</h3>
</body>
</html>

A simple response page that is the final result returned by the action class.

When we run the application, we get following response pages.

Struts2-execAndWait-input

Struts2-execAndWait-wait-result

Struts2-execAndWait-success

That’s all for execAndWait interceptor example, token is also used in Struts2 token interceptor to handle double form submission.

Read more about interceptors at Struts2 interceptors tutorial.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current day month ye@r *