سؤال

I have a login screen where some user input validation will happen, 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:

1) For an authenticated user, the wait.jsp page is getting displayed but the redirection to Welcome.jsp is not happening.

2) 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)
هل كانت مفيدة؟

المحلول

  1. execAndWait causes the action to be executed in a new thread.
  2. Since 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
  3. 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.

EXAMPLE

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);
    }

}

نصائح أخرى

NPE is happening because the action with execAndWait interceptor is running in a separate thread and you are calling getText method which uses ActionContext. ActionContext is thread local which means that values stored in the ActionContext are unique per thread.

In order to show success page after process is over you need to refresh page time to time. In examples it is done with meta http-equiv="refresh".

<meta http-equiv="refresh" content="5;url=<s:url includeParams="all" />"/> 

The key "error.login" is not found in the resources supplied with your action or application or you have used wrong locale. That means that you have not i18n resources configured. To resolve your issue you need to create LoginAction.properties file and put the key inside it

error.login = Error login

If you are using global properties file that is not seen from your post, then add this key there.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top