How to handle exceptions in Struts
- Details
- Written by Nam Ha Minh
- Last Updated on 31 July 2019   |   Print Email
Struts provides an easy way for handling uncaught exceptions which might be thrown during execution of action classes. Uncaught exceptions are ones which are not caught by the regular try-catch clause. There are two methods for handing uncaught exceptions in Struts:
- Global exception handling: specifies exception mappings (exception type - view name) which apply to all action classes in a Struts package.
- Exception handling per action: specifies exception mappings which apply to a specific action class.
Both methods require adding exception mappings in struts.xml configuration file. Let’s go through each method in details.
1. Global exception handling in Struts
Add the following code snippet just after the <package> element in struts.xml file:
<global-results> <result name="error">/Error.jsp</result> </global-results> <global-exception-mappings> <exception-mapping exception="java.lang.Exception" result="error"/> </global-exception-mappings>
The <global-results> element defines global view names. Here the view named “error” is mapped with the actual view page “/Error.jsp”.
The <global-exception-mappings> element specifies a set of <exception-mapping> element which maps an exception type to a view name. Here the exception of type java.lang.Exception is mapped to the view name “error”. That means when any uncaught exception of type java.lang.Exception or its sub types is thrown, Struts will redirect users to the view page mapped with the name “error”.
In the Error.jsp page we can access information of the exception as follows:
Exception name: <s:property value="exception"/> Exception stack trace: <s:property value="exceptionStack"/>
Or simpler with expression language (EL):
Exception name: ${exception} Exception stack trace: ${exceptionStack}
Let’s see a complete example in action. Consider the following Struts action class:
package net.codejava.struts; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.interceptor.ServletRequestAware; import com.opensymphony.xwork2.ActionSupport; public class SumAction extends ActionSupport implements ServletRequestAware { private HttpServletRequest request; private int sum; public int getSum() { return sum; } public String execute() { // an exception might be thrown here if x/y is not a number int x = Integer.parseInt(request.getParameter("x")); int y = Integer.parseInt(request.getParameter("y")); sum = x + y; return SUCCESS; } @Override public void setServletRequest(HttpServletRequest request) { this.request = request; } }
The execute() method parses two numbers x and y from the request and produces sum of both. In this case, an exception might be thrown if either x or y is not a number (a java.lang.NumberFormatException exception is raised).
Code of the Error.jsp page:
<%@ 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> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Error page</title> </head> <body> <center> <h1>Sorry, unexpected exception occurred:</h1> <h2>Exception name: ${exception}</h2> </center> </body> </html>
Code of the JSP page which will be displayed when the execute() method returns successfully (Sum.jsp):
<%@ 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>Sum Result</title> </head> <body> <center> <h1>Sum is: ${sum}</h1> </center> </body> </html>
Code of struts.xml file:
<?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="Struts2ExceptionHandling" extends="struts-default"> <global-results> <result name="error">/Error.jsp</result> </global-results> <global-exception-mappings> <exception-mapping exception="java.lang.Exception" result="error"/> </global-exception-mappings> <action name="sum" class="net.codejava.struts.SumAction"> <result name="success">/Sum.jsp</result> </action> </package> </struts>
The following screenshot is output of the application when passing two numeric parameters:
If either a parameter is not a number, an exception is thrown and our Error.jsp page will be displayed instead:
Of course we can specify multiple exception mappings as follows:
<global-results> <result name="error">/Error.jsp</result> <result name="dbError">/DBError.jsp</result> </global-results> <global-exception-mappings> <exception-mapping exception="java.lang.Exception" result="error"/> <exception-mapping exception="java.sql.SQLException" result="dbError"/> </global-exception-mappings>
The mapping with most specific exception type will take precedence.
NOTES: With global exception handling method, the view names specified in the <exception-mapping> elements must be declared in the <global-results> element.
2. Exception handling per action in Struts
This method specifies the <exception-mapping> elements inside the <action> element, for example:
<action name="connectDB" class="net.codejava.struts.ConnectDBAction"> <exception-mapping result="dbError" exception="java.sql.SQLException" /> <result name="success">/DBConnect.jsp</result> <result name="dbError">/DBError.jsp</result> </action>
That tells Struts to redirect the users to the view “dbError” when an exception of type java.sql.SQLException (or its sub type) is thrown inside the action class ConnectDBAction.
Code of the action class (ConnectDBAction.java) file:
package net.codejava.struts; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import com.opensymphony.xwork2.ActionSupport; public class ConnectDBAction extends ActionSupport { public String execute() throws SQLException, ClassNotFoundException { String databaseURL = "jdbc:mysql://localhost:3306/test?user=root&password=secret"; Connection conn = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(databaseURL); } finally { if (conn != null) { conn.close(); } } return SUCCESS; } }
The action method execute() simply tries to connect to a MySQL database. If success, return the view named “success”.
Code of the success view (DBConnect.jsp):
<%@ 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>Database Connection Result</title> </head> <body> <center> <h1>Connected to database successfully!</h1> </center> </body> </html>
Code of the error page (DBError.jsp):
<%@ 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>Database Error page</title> </head> <body> <center> <h1>Sorry, a database exception occurred:</h1> <h2>Exception name: ${exception}</h2> </center> </body> </html>
Output when connecting to the database successfully:
Output when an exception is thrown (e.g the username/password is incorrect):
NOTE: In this exception handling per action method, the view names specified in the <exception-mapping> elements can refer to either views declared in the enclosing action or in the <global-results> element.
3. Logging exceptions in Struts
In addition to redirect the users to a specific error handling page, Struts also allows us to log exceptions in servlet container’s console and/or in log files. To enable logging exceptions, place the following code just after the <package> element:
<interceptors> <interceptor-stack name="appDefaultStack"> <interceptor-ref name="defaultStack"> <param name="exception.logEnabled">true</param> <param name="exception.logLevel">ERROR</param> </interceptor-ref> </interceptor-stack> </interceptors>
That declares an interceptor named “appDefaultStack” which extends from the Struts default stack and configures two attributes of the Struts exception interceptor: logEnabled and logLevel. Behind the scene, the Struts exception interceptor looks at the exception mappings, and redirects the users to the appropriate view when an exception is raised. But the exceptions logging is not enabled by default. The logLevel can be one of the following values: trace, debug, info, warn, error and fatal.
When enabled, Struts logs the exceptions in servlet container’s console as in the following example:
If a file logging is configured (such as log4j), the exceptions are also written into the log files.
So far the struts.xml file used in this tutorial’s examples is as follows:
<?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="Struts2ExceptionHandling" extends="struts-default"> <interceptors> <interceptor-stack name="appDefaultStack"> <interceptor-ref name="defaultStack"> <param name="exception.logEnabled">true</param> <param name="exception.logLevel">ERROR</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="appDefaultStack" /> <global-results> <result name="error">/Error.jsp</result> </global-results> <global-exception-mappings> <exception-mapping exception="java.lang.Exception" result="error"/> </global-exception-mappings> <action name="sum" class="net.codejava.struts.SumAction"> <result name="success">/Sum.jsp</result> </action> <action name="connectDB" class="net.codejava.struts.ConnectDBAction"> <exception-mapping result="dbError" exception="java.sql.SQLException" /> <result name="success">/DBConnect.jsp</result> <result name="dbError">/DBError.jsp</result> </action> </package> </struts>
NOTE: If a same exception mapping is declared both globally and per action, then the action-specific exception mapping will take precedence.
Related Java Exception Handling Tutorials:
- Getting Started with Exception Handling in Java
- How to Handle Error in Web.xml for Java web applications
- How to handle exceptions in JSP
Other Struts Tutorials:
- Introduction to Struts 2 framework
- Struts beginner tutorial (Eclipse + Tomcat + XML)
- Struts Beginner Tutorial with Annotations
- Struts beginner tutorial with Convention Plugin (zero-configuration)
- Send e-mail with attachments in Struts
- Struts File Upload Tutorial
- Struts - Spring - Hibernate Integration Tutorial
Comments