I have a login
screen where some user's input validation is happened, the user will be authenticated and finally redirected to the welcome
screen.
Below is the interceptor definition for the LoginAction
:
<package name="default" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="myInterceptor"
class="com.interceptor.MyInterceptor"></interceptor>
<interceptor-stack name="newStack">
<interceptor-ref name="myInterceptor"/>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="execAndWait">
<param name="delay">100</param>
<param name="delaySleepInterval">500</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="login"
class="com.action.LoginAction">
<interceptor-ref name="newStack"/>
<result name="success">common/Welcome.jsp</result>
<result name="wait">common/wait.jsp</result>
<result name="error">Login.jsp</result>
<result name="input">Login.jsp</result>
</action>
</package>
Below is the execute method of LoginAction
:
if (isUserAuthenticated) {
// Some background processing for logging purpose
return "success";
} else {
addActionError(getText("error.login"));
return "error";
}
I am having a couple of problems with this code:
For an authenticated user, the wait.jsp
page is getting displayed, but the redirection to Welcome.jsp
is not happening.
for an unauthenticated user, I am getting the below exception:
java.lang.NullPointerException
at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:361)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:208)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:123)
at com.opensymphony.xwork2.ActionSupport.getText(ActionSupport.java:103)
at com.infy.action.LoginAction.execute(LoginAction.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:450)
at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:289)
at org.apache.struts2.interceptor.BackgroundProcess$1.run(BackgroundProcess.java:57)
at java.lang.Thread.run(Thread.java:662)
execAndWait
causes the action to be executed in a new thread.ActionContext
is ThreadLocal
, the new thread will not get the values stored in the parent thread version of ActionContext
. Every thread has a unique version of ActionContext
getText()
will throw a NPE when it tries to execute in the new thread because it depends on ActionContext
To fix this, you need to copy the parent threads ActionContext
into the execAndWait
thread. You can do this by extending the BackgroundProcess
class, implementing the beforeInvocation()
and afterInvocation()
methods, and extending ExecuteAndWaitInterceptor
, implementing the getNewBackgroundProcess()
method.
public class YourExecAndWaitInterceptor extends ExecuteAndWaitInterceptor {
private static final long serialVersionUID = 1L;
/**
* {@inheritDoc}
*/
@Override
protected BackgroundProcess getNewBackgroundProcess(String arg0, ActionInvocation arg1, int arg2) {
return new YourBackgroundProcess(arg0, arg1, arg2, ActionContext.getContext());
}
}
public class YourBackgroundProcess extends BackgroundProcess {
private final ActionContext context;
public YourBackgroundProcess(String threadName, ActionInvocation invocation, int threadPriority, ActionContext context) {
super(threadName, invocation, threadPriority);
this.context = context;
}
/**
* {@inheritDoc}
*/
@Override
protected void beforeInvocation() {
ActionContext.setContext(context);
}
/**
* {@inheritDoc}
*/
@Override
protected void afterInvocation() {
ActionContext.setContext(null);
}
}