I have Struts 2 configured to redirect any java.lang.Exception to a special Action which logs the exception. My redirection works, but my Action always gets a null exception (even when I explicitly throw an Exception). Here is my struts.xml file:
<global-results>
<result name="errHandler" type="chain">
<param name="actionName">errorProcessor</param>
</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="errHandler" />
</global-exception-mappings>
<action name="errorProcessor" class="myErrorProcessor">
<result name="error">/error.jsp</result>
</action>
<action name="throwExceptions" class="throwExceptions">
<result name="success">done.jsp</result>
</action>
In my error processor, I have the following:
public class myErrorProcessor extends ActionSupport {
private Exception exception;
public String execute() {
System.out.println("null check: " + (exception == null));
return "error";
}
public void setException(Exception exception) {
this.exception = exception;
}
public Exception getException() {
return exception;
}
}
In the throwsException class, I have the following:
public String execute() {
int x = 7 / 0;
return "success";
}
When I run my program, the Exception handler always gets a null exception. I am using the chain type to redirect to the exception handler. Do I need to implement some sort of ExceptionAware interface? Is the Struts 2 exception setter called something besides setException?
Note: I was trying to follow this tutorial when writing this program.
Using struts2 version 2.3.1.2 I do not get a null exception in my error handler everything works as advertised. Ensure you are using an unmodified defaultStack.
As far as credible sources, we can reference the interceptor documentation for ExceptionMappingInterceptor and Chaining Interceptor. The ExceptionMappingInterceptor pushes an ExceptionHolder onto the value stack which in turn exposes an exception property. The chaining interceptor copies all the properties on the value stack to the target. Since ExceptionHolder is on the stack if there is a setter for Exception it will be set.
Here are all the files which produce a working example:
struts.xml (kept quite similar to the questions):
<?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 name="struts.devMode" value="true" />
<constant name="struts.ui.theme" value="simple" />
<package name="kenmcwilliams" namespace="/" extends="struts-default">
<global-results>
<result name="errHandler" type="chain">
<param name="actionName">errorProcessor</param>
</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="errHandler" />
</global-exception-mappings>
<action name="errorProcessor" class="com.kenmcwilliams.test.myErrorProcessor">
<result name="error">/WEB-INF/content/error.jsp</result>
</action>
<action name="throwExceptions" class="com.kenmcwilliams.kensocketchat.action.Bomb">
<result name="success">/WEB-INF/content/bomb.jsp</result>
</action>
</package>
</struts>
MyErrorProcessor.java
package com.kenmcwilliams.test;
import com.opensymphony.xwork2.ActionSupport;
public class MyErrorProcessor extends ActionSupport {
private Exception exception;
@Override
public String execute() {
System.out.println("Is exception null: " + (exception == null));
System.out.println(""
+ exception.getMessage());
return "error";
}
public void setException(Exception exceptionHolder) {
this.exception = exceptionHolder;
}
public Exception getException() {
return exception;
}
}
Bomb (just throws RuntimeException):
package com.kenmcwilliams.kensocketchat.action;
import com.opensymphony.xwork2.ActionSupport;
public class Bomb extends ActionSupport{
@Override
public String execute() throws Exception{
throw new RuntimeException("Hello from Exception!");
}
}
View for bomb (/WEB-INF/content/bomb.jsp) [Never reachable]
<%@taglib prefix="s" uri="/struts-tags"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>The Bomb!</title>
</head>
<body>
<h1>The Bomb!</h1>
</body>
</html>
View for error (/WEB-INF/content/error.jsp)
<%@taglib prefix="s" uri="/struts-tags"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Global Error Handler</title>
</head>
<body>
<h1>Global Error Handler</h1>
<s:property value="exception.stackTrace"/>
</body>
</html>
Output:
I see error.jsp render and I see the following printed on the glassfish console:
INFO: Is exception null: false
INFO: Hello from Exception!
I was having the same issue you were having!
While it is much cleaner to have the exception
field populated by Struts, it appears that this field population is not happening (as you stated). To get around this, I removed the exception field (and its getter/setter) from my Exception handler class (your myErrorProcessor
class) and replaced that with a method to "manually" extract the exception from the ValueStack
:
/**
* Finds exception object on the value stack
*
* @return the exception object on the value stack
*/
private Object findException() {
ActionContext ac = ActionContext.getContext();
ValueStack vs = ac.getValueStack();
Object exception = vs.findValue("exception");
return exception;
}
I can then use instanceof
to make sure the exception Object
is the type that I was expecting and then handle that exception accordingly (like writing messages found in a custom Exception
object to a database).
Let me know how this works for you! :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With