Results 1 to 6 of 6

Thread: refresh issue - command object becomes null

  1. #1
    Join Date
    Feb 2006
    Posts
    13

    Default refresh issue - command object becomes null

    Hi all,

    I am having an unusual problem with my Spring MVC setup and it's doing my head in. It seems that when I refresh a page loaded by a form-controller the command object no longer exists in the session.

    Let me explain:
    I have Page1FormController and Page2FormController. Both extend the SimpleFormController with the command object being an "application" (ie - an Application object).

    So with Page1FormController I have:
    Code:
    public class Page1FormController extends SimpleFormController {
      public Page1FormController () {
        super.setCommandName("application");
        super.setSessionForm(true);
      }
    
      ...
    
      protected Object formBackingObject(HttpServletRequest request) throws ServletException {
        // Create a new application here.
        Application application = new Application(); 
    
        return application;
      }
    
      protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception {
        ...
        // Place application in session to access on second page.
        request.getSession().setAttribute("application", application);
        return new ModelAndView("redirect:/page2FormController.htm");
      }
    }
    And then in Page2FormController I have:
    Code:
    public class Page2FormController extends SimpleFormController {
      public Page2FormController() {
        super.setCommandName("application");
        super.setSessionForm(true);
      }
    
      ...
    
      protected Object formBackingObject(HttpServletRequest request) throws ServletException {
        // This works when I come from page1, but fails when i'm already on
        // page2 and hit the browser refresh button:
        Application application = (Application) session.getAttribute("application");
        
        logger.debug("application = " + application);
    
        return application;
      }
    }
    Now the problem is that when i get to page2FormController.htm the "application" object exists in the session. But when I refresh that page it's somehow disappeared (null).

    I don't see how coming from page1 can make such a difference since it's a redirect i'm performing anyway.

  2. #2
    Join Date
    Feb 2006
    Posts
    13

    Default temporary solution

    i've come up with a temporary solution which seems to do the trick. it's really a hack, but until someone can help me i guess i'll have to stick with this.

    In Page2FormController:
    Code:
    protected Object formBackingObject(HttpServletRequest request) throws ServletException {
      Application application = (Application) session.getAttribute("application");
    
      // This is the hack.  
      if (application != null) {
        request.getSession().setAttribute("application2", application);
      } else {
        Application application2 = (Application) session.getAttribute("application2");
        application = application2;
      }
    
      return application;
    }
    Basically, I protect the application session variable by storing it as another name (application2) and retrieve that when application is null.

  3. #3
    Join Date
    May 2005
    Location
    California, US
    Posts
    735

    Default

    You could also set up a bean that has session scope.

    http://static.springframework.org/sp...factory-scopes

    I just did this and it's quite slick. The bean is injected by spring and its internal state is kept in the session so each object that gets it injected gets what's in it. The first object (e.g., some controller) that gets it can set it up, calling its setters, then the rest can use its getters and setters.

    applicationContext.xml:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.1.xsd">
    
        <bean id="userMeta" class="jmemento.domain.user.impl.UserMeta" scope="session">
           <aop:scoped-proxy proxy-target-class="false" />
        </bean>
    myApp-servlet.xml:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    
        <bean id="multipartResolver"
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        </bean>
    
        <!--
            ## convention over configuration handler
        -->
        <bean
            class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
    
        <!--
          ## controller beans
        -->
    
        <bean class="jmemento.web.controller.user.UserHomeController">
            <constructor-arg ref="userMeta" />
        </bean>
    web.xml:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
        version="2.4">
    
        <display-name>myApp</display-name>
    
        <!--
            ## Location of the Log4J config file, for initialization and
            ## refresh checks.  Applied by Log4jConfigListener.
        -->
        <context-param>
            <param-name>
                log4jConfigLocation
            </param-name>
    
            <param-value>
                /WEB-INF/log4j.xml
            </param-value>
        </context-param>
    
        <listener>
            <listener-class>
                org.springframework.web.util.Log4jConfigListener
            </listener-class>
        </listener>
    
        <!-- bootstraps the WebApplicationContext -->
        <!-- must come after the Log4jConfigListener -->
        <listener>
            <listener-class>
                org.springframework.web.context.ContextLoaderListener
            </listener-class>
        </listener>
    
        <!-- listener for beans with session scope -->
        <listener>
            <listener-class>
                org.springframework.web.context.request.RequestContextListener
            </listener-class>
        </listener>
    UserHomeController.java:
    Code:
    public final class UserHomeController extends AbstractCommandController {
        private final transient Log log = LogFactory.getLog(getClass());
    
        private final IUserMeta userMetaSession;
    
        /**
         * @param _userMetaSession
         */
        public UserHomeController(final IUserMeta _userMetaSession) {
            if (_userMetaSession == null)
                throw (new IllegalArgumentException("userMetaSession can't be null"));
    
            userMetaSession = _userMetaSession;
    
            setSupportedMethods(new String[] {
                METHOD_GET
            });
    
            // no longer used
            setCommandClass(UserMeta.class);
        }
    
        @Override
        protected ModelAndView handle(final HttpServletRequest request,
                final HttpServletResponse response, final Object command,
                final BindException errors) throws Exception {
            // no longer using the command
            log.debug(String.format("command: %s", command));
    
            log.debug(String.format("user: %s", userMetaSession));
    
            if (userMetaSession.getId() == null)
                return (new ModelAndView("notsignedin"));
    
            final ModelAndView mav = new ModelAndView();
    
            mav.addObject("user", userMetaSession);
    
            return (mav);
        }
    }

  4. #4
    Join Date
    Feb 2006
    Posts
    13

    Default interesting

    Very interesting solution you've got there lumpynose, i'll have to try it out today.

    I'm assuming that because you've declared "userMeta" as a session scoped bean then "UserHomeController" (which relies on userMeta in its constructor) is no longer a singleton and hence gets instantiated with every new session. I hope I'm correct here.

  5. #5
    Join Date
    May 2005
    Location
    California, US
    Posts
    735

    Default

    Quote Originally Posted by KENTOSI View Post
    Very interesting solution you've got there lumpynose, i'll have to try it out today.

    I'm assuming that because you've declared "userMeta" as a session scoped bean then "UserHomeController" (which relies on userMeta in its constructor) is no longer a singleton and hence gets instantiated with every new session. I hope I'm correct here.
    Read the section of the reference manual I linked to. If I remember correctly the UserHomeController is still a singleton but the userMetaSession bean is wrapped by a proxy and its methods return the correct values for the session, because it's the proxy's methods that are really being used. That's my rough understanding of how it works.

  6. #6
    Join Date
    Nov 2005
    Location
    Reutlingen, Germany
    Posts
    2,098

    Default

    Quote Originally Posted by lumpynose View Post
    Read the section of the reference manual I linked to. If I remember correctly the UserHomeController is still a singleton but the userMetaSession bean is wrapped by a proxy and its methods return the correct values for the session, because it's the proxy's methods that are really being used. That's my rough understanding of how it works.
    Just want to confirm your understanding Since the session-scoped proxy is a singleton the controller can remain a singleton as well. The proxy works internally with ThreadLocals, so there is no threading issue.

    Joerg
    This post can contain insufficient information.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •