3

I'm having trouble with my setup, my login has passed the validation but execute function is not being called

LoginAction.java:

@Override
public String execute() throws Exception {

    System.out.println("5");
    String username = blogUser.getUsername();
    String password = blogUser.getPassword();
    blogUser = blogUserService.getUserByLogin(username, password);
    System.out.println("6");
    sessionMap.put(Constants.SESSION_USERNAME, blogUser.getUsername());
    System.out.println("7");
    sessionMap.put(Constants.SESSION_USERID, blogUser.getUserId());
    System.out.println("return:success");
    return SUCCESS;
}

@Override
public void validate() {
    System.out.println("1");
    String username = blogUser.getUsername();
    String password = blogUser.getPassword();
    System.out.println("username:"+username + ", password:"+password);
    if (username == null & password == null) {
        System.out.println("22");
        addFieldError("blogUser.username","");
    } else if (username == null || password == null) {
        System.out.println("2");
        addFieldError("blogUser.username","Invalid Login");
    } else if (!blogUserService.checkLogin(username, password)) {
        System.out.println("3");
        addFieldError("blogUser.username","Invalid Login");
    }
    System.out.println("4");
}

public String postLogin() throws Exception {
    System.out.println("77");
    return LOGIN;
}

struts.xml:

    <action name="login" class="loginActionBean" >
        <result name="input" type="tiles">/login.tiles</result>
        <result name="none" type="tiles">/login.tiles</result>
        <result name="login" type="tiles">/login.tiles</result>
        <result name="success" type="redirectAction">postPreviewAction</result>
        <result name="error" type="tiles">/login.tiles</result>
    </action>
    
    <action name="doLogin" class="loginActionBean" method="postLogin">
        <result name="login" type="tiles">/login.tiles</result>
        <result name="input" type="redirectAction">login</result>
    </action>

login.jsp:

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<div>
    <h2>Users Login</h2>
    <s:form action="login" method="post">
        <s:textfield label="Username" name="blogUser.username" />
        <s:password label="Password" name="blogUser.password" />
        <s:submit value="Login" />
    </s:form>
</div>

I can see only "4" being printed (meaning it passed the validation) but that's it, it doesn't go to "5"

EDIT:

added tiles.xml snippet

<definition name="/login.tiles" extends="baseLayout">
    <put-attribute name="body" value="/login.jsp" />
</definition>
Roman C
  • 49,761
  • 33
  • 66
  • 176
Jiro Manio
  • 137
  • 10
  • What scope are you using for action beans? Is your tiles definition really named `/login.tiles`? – Aleksandr M Mar 30 '16 at 11:58
  • I don't have a scope setup, mostly default. I'll edit it in my snippet for the tiles.xml – Jiro Manio Mar 30 '16 at 12:05
  • 1
    And default is `singleton` which is wrong for action, should be `prototype`. – Aleksandr M Mar 30 '16 at 12:06
  • 1
    I suspect the problem is in your struts.xml wiring, it seems too convoluted. My open-source [blogserver](https://github.com/gmazza/tightblog), while unfinished, presently uses Struts, Tiles & Spring security to handle logins, it may have something you can leverage. – Glen Mazza Mar 30 '16 at 23:16
  • Aleksandr, RomanC thanks I'll adjust the scope Glen i too think that its the struts.xml thats the problem, I just can't figure it out. ill look at your github, thanks for the link – Jiro Manio Mar 31 '16 at 00:42
  • 1
    @AleksandrM kindly post your comment as an answer so I can accept it. I did scrap it first, did some other things, then put the scope, then tried it again and it worked. Not sure where/what I did to fix it but your comment did help – Jiro Manio Apr 04 '16 at 03:15

2 Answers2

2

From the Struts2 Spring Plugin documentation:

Normally, in struts.xml you specify the class for each Action. When using the default SpringObjectFactory, the framework will ask Spring to create the Action and wire up dependencies as specified by the default auto-wire behavior.

Meaning you don't need to create Spring beans out of your actions.

However, sometimes you might want the bean to be completely managed by Spring. This is useful, for example, if you wish to apply more complex AOP or Spring-enabled technologies, such as Acegi, to your beans. To do this, all you have to do is configure the bean in your Spring applicationContext.xml and then change the class attribute from your`Action in the struts.xml to use the bean name defined in Spring instead of the class name.

Struts2 itself creates new instance of action for each request, so actions are not singletons. If you create a Spring bean out of action then give it a proper scope (e.g. scope="prototype"), because:

By default, a bean will be a singleton, unless the bean has a parent bean definition in which case it will inherit the parent's scope.

The loginActionBean example declaration:

<bean id="loginActionBean" class="some.package.LoginActionBean" scope="prototype" />
Aleksandr M
  • 24,264
  • 12
  • 69
  • 143
1

The action named doLogin (should have the proper name like showLogin) is the action that shows the login page. It shouldn't be validated because it will always fail. You have to remove this from the action config

<result name="input" type="redirectAction">login</result>

And the action method should be excluded from validation. You can configure validation interceptor to exclude this method but another way to do it just put @SkipValidation annotation on the method.

@SkipValidation
public String showLogin() throws Exception { 
   System.out.println("77");
   return LOGIN;
}

The action named login has a few redundant results that could be removed

<result name="none" type="tiles">/login.tiles</result>
<result name="login" type="tiles">/login.tiles</result>        
<result name="error" type="tiles">/login.tiles</result>

Note: that validation by default is called on every action method from the action class unless it excluded from validation or doesn't have validation interceptor configured.

The final configuration:

<action name="login" class="loginActionBean" >
    <result name="input" type="tiles">/login.tiles</result>
    <result type="redirectAction">postPreviewAction</result>
</action>

<action name="showLogin" class="loginActionBean" method="showLogin">
    <result name="login" type="tiles">/login.tiles</result>
</action> 
Roman C
  • 49,761
  • 33
  • 66
  • 176
  • thanks for the notes. I actually did the things you posted here before i saw your answer. The issue was that, even if the doLogin method needs some improvement, the execute method was not being called. I was already in the login page which the doLogin method supplied which calls the action for the execute – Jiro Manio Apr 04 '16 at 03:18
  • @JiroManio Didn't get actually what are you saying. The execute method is called by the action invocation, and *not* in the other way. – Roman C Apr 21 '16 at 07:52
  • yes action invocation calls it. but that is after the validate function. if there is a addFieldError function called, it will not the execute function. the issue was that when the action passed the validate function, meaning there was no addFieldError called, it did not continue to the execute function, which is supposed to be automatic. it just stops at the end of the validate function. TLDR: the action calls STOPS at the validate function and does not continue to the execute function – Jiro Manio Apr 21 '16 at 08:11
  • This means that the action *has* errors, regardless of if you added field errors or not. The errors could be added by the validation or any other ways, including manually added errors, but the flow is the same. – Roman C Apr 21 '16 at 08:17
  • and thats where my problem was. I cannot trace which boolean (I'm assuming) was triggered for the action to not proceed to the execute function because it passed all my ifs in the validate function – Jiro Manio Apr 21 '16 at 08:25
  • But you have traced each boolean and one of the condition worked but the rest of the code you have not posted. All that you gave me an opportunity to poke a finger into the sky, OK. – Roman C Apr 21 '16 at 08:57
  • that was the whole code in that action, the remaining ones are getter/setters. why would I kill myself by not helping the people that wants to help me O_O. and no, I have passed all the condition so it only printed 4 from sout which i provided on the original post – Jiro Manio Apr 21 '16 at 09:05
  • May be 4 is printed from another method, not `validate()`? Because the validate method prints 1 then 4. – Roman C Apr 21 '16 at 09:11
  • my bad, i forgot there is a 1 in the very first line. i suspect the issue was that singleton default was reseting the value or something so i was not going through the execute part. I have already fixed it by adding the scope="prototype" in my application.xml. thanks for the additional notes – Jiro Manio Apr 21 '16 at 09:13
  • Further if someone asks why you added this to your code, you will stand with your mouth open, because it only saved you from the mistakes that you was too lazy to post when asked the question simply omitted. – Roman C Apr 21 '16 at 09:27