Announcement Announcement Module
Collapse
No announcement yet.
Spring 3.0 Portlet MVC : Event Handling Problem Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring 3.0 Portlet MVC : Event Handling Problem

    I am pretty new to annotation based Spring 3.0 Portlet MVC development and have spent the last few days attempting to come up to speed.

    I have 2 portlets. The 1st generates a particular event during a certain action handler which is consumed by the 2nd. This is working.

    I have an event handler (onScorePersonInvocation()) in a controller (which is a part of portlet 2) in which I want to refresh a session bean before rendering. I assumed (probably incorrectly) that I could refresh a bean in the passed model and that this would then be made visible to the associated view. However, this does not seem to be the case.

    My controller code from portlet 2 is below.

    Code:
    @Controller
    @RequestMapping("VIEW")
    @SessionAttributes("usageInfo")
    public class UsageController {
    
        // Services
        Usage usageService;
    
        // User refresh ------------------------------------------------------------
    
    	@ActionMapping(params="action=refresh")
        public void refreshAction(Model model) {
    
            refreshUsageInfo(model);
        }
    
        @RenderMapping
        public String refreshRender(Model model) {
    
            return "usage";
        }
    
        // Event Handling ----------------------------------------------------------
    
        @EventMapping("scorePersonInvocation")
        public void onScorePersonInvocation(Model model) {
    
            refreshUsageInfo(model);
        }
    
        // Privates ----------------------------------------------------------------
    
        private void refreshUsageInfo(Model model) {
            ArrayList<DayUsage> usage = usageService.getUsage(5);
            model.addAttribute("usageInfo", usage);
        }
    
        public void setUsageService(Usage usageService) {
            this.usageService = usageService;
        }
    }
    Can anybody explain why this does not work? Everything works without issue when I invoke the action handler: refreshAction().

  • #2
    Could this be a bug?

    Does anyone think this might be a bug? Am I right in thinking this is pretty new stuff, specific to the 3.0 release?

    Comment


    • #3
      Two thoughts:

      1) Make sure the portlet is in fact being called to re-render when your event occurs. According to the Spec it should, but if it's not that would be the fault of the portlet container, not Spring.

      2) It's definitely possible that Spring is not updating the session attribute properly from event requests. You are correct that the JSR 286 features are new in Spring 3 and may still have issues. If you have confirmed that it is rendering, but the session attribute was not updated by the event handler, then I think you should go ahead and open a JIRA for this issue.

      Comment


      • #4
        So, was this ever filed as a bug?

        I can't find anything in the jira database, but I'm new to Spring, so I may not be looking in the right place. I can't see that it was filed (or fixed).

        I have seen the above issue as well in Spring 3.0 (3.0.0.Release).

        Also, I have a related issue. I exit a portlet, and then return to the same portlet via an event. On the return, a session attribute (created and added to the model prior to leaving the first time) exists in the Event Handler w/ the correct values. But then when the renderer for that same request is invoked, the session attribute has been lost and a new bean is instantiated from scratch.

        For example, when I run the below, the first logger output line in the event handler gives the correct value for savedValue. But on entry to the renderer, the value shown is -1 (the initializing default value), and several other values (not shown) are null. A new bean has been initialized, and the old one is apparently lost.

        I think this should work, but it isn't and I can't see why. Is this a bug or am I missing something?


        Code:
        public class seletionBean {
          int savedValue = -1;  /* will be set to non-null prior to exit. */
        
          /* savedValue getter and setter */
        }
        
        -----
        
        @Controller
        @RequestMapping("VIEW")
        @SessionAttributes(value={"commonBean"})
        public class HomeScreenRequestHandler {
        
        	@EventMapping(value = "ReturnToHome")
        	public void returnToHome(Model model, 
        		@ModelAttribute("commonBean") selectionBean commonBean,
        		EventRequest request, EventResponse response) 
        	{
        		logger.debug("Value in EventHandler: " + commonBean.getSavedValue);
        
        		/* Same behavior w/ or w/o the addAttribute of the commonBean line: */
        		model.addAttribute("commonBean", commonBean);
        
        		model.addAttribute("TestCase", "value1");
        		
        		response.setRenderParameter("nextScreen", "selectStyles");
        	}
        
        	@RenderMapping(params = "nextScreen=selectStyles")
        	public String showSelectStyles(Model model, @ModelAttribute("commonBean") selectionBean commonBean){
        
        		logger.debug("Value in EventHandler: " + commonBean.getSavedValue);
        		
        		logger.debug("Model contains TestCase: " + (model.asMap().containsKey("TestCase") ? "yes" : "no"));
        
        		return("selectStyles.jsp");
            }
        }
        (This code also contains my version of the bug mentioned by the OP. TestCase does not exist in the Model at the render stage, and I assume it should, since I just added it in the event.)

        Comment


        • #5
          TestCase is being added to the model but is not in @SessionAttributes. You are relying on the implicit model being preserved between the event phase and the render phase.

          The implicit model is saved at the end of the action phase and made available to subsequent renders but it isn't saved after the event phase. See the end of AnnotationMethodHandlerAdapter.invokeHandlerMethod .

          It isn't clear to me if this is by design or if it just hasn't been updated for JSR286.

          Comment


          • #6
            Originally posted by andyc View Post
            TestCase is being added to the model but is not in @SessionAttributes. You are relying on the implicit model being preserved between the event phase and the render phase.

            The implicit model is saved at the end of the action phase and made available to subsequent renders but it isn't saved after the event phase. See the end of AnnotationMethodHandlerAdapter.invokeHandlerMethod .

            It isn't clear to me if this is by design or if it just hasn't been updated for JSR286.
            I couldn't have found it myself, but I assume you're referring to the lines
            Code:
            private ModelAndView invokeHandlerMethod(
            . . . .
            	// Expose implicit model for subsequent render phase.
            	if (response instanceof ActionResponse && !implicitModel.isEmpty()) {
            		ActionResponse actionResponse = (ActionResponse) response;
            		try {
            			actionResponse.setRenderParameter(IMPLICIT_MODEL_ATTRIBUTE, Boolean.TRUE.toString());
            			request.getPortletSession().setAttribute(IMPLICIT_MODEL_ATTRIBUTE, implicitModel);
            		}
            . . . .
            , and that perhaps it should be ActionResponse || EventResponse.

            That would deal w/ the original problem -- so I guess I'll file it in the JIRA and see what replies come out.

            That deals w/ the original poster's problem. Though not my related one, in which an attribute that IS in the @SessionAttributes is still lost after returning from an event. Any thoughts? Two Issues?

            Comment


            • #7
              Yes, that's the code I meant. ActionResponse and EventResponse have a common superclass - StateAwareResponse.

              As for saving @SessionAttributes, I can't spot the problem just from code scanning. They should get saved in HandlerMethoInvoker.updateModelAttributes which gets called on the line before the code you pasted. I think they are supposed to be restored at the top of HandlerMethodInvoker.invokeHandlerMethod, though I haven't worked out how actualSessionAttributeNames has been populated at this point. Anyway, that's where I would be setting breakpoints!

              Comment


              • #8
                Solved the &quot;Losing the Session Bean&quot; problem -- perhaps very obscure bug.

                Found the issue w/ my second problem w/ the Session Attribute being initialized. I was simplifying my code when I created the post -- I'm sorry -- I now realize I simplified it too much to make diagnosing this reasonable.

                The code that is in two files in the same project.

                Code:
                package com.generic.orderentry.skuselection.requesthandler;
                
                @Controller
                @RequestMapping("VIEW")
                @SessionAttributes(value={"commonBean"})
                public class HomeScreenRequestHandler {
                
                        /* All Event Handlers And Action Handlers and Renderers. */
                
                	@EventMapping(value = "ReturnToHome")
                	public void returnToHome(Model model, 
                		@ModelAttribute("commonBean") selectionBean commonBean,
                		EventRequest request, EventResponse response) 
                	{
                		logger.debug("Value in EventHandler: " + commonBean.getSavedValue);
                
                		/* Same behavior w/ or w/o the addAttribute of the commonBean line: */
                		model.addAttribute("commonBean", commonBean);
                
                		model.addAttribute("TestCase", "value1");
                		
                		response.setRenderParameter("nextScreen", "selectStyles");
                	}
                and

                Code:
                package com.generic.orderentry.skuselection.requesthandler;
                
                @Controller
                @RequestMapping("VIEW")
                public class SelectStyleScreenRequestHandler {
                        /* All resource request handlers */
                
                @RenderMapping(params = "nextScreen=selectStyles")
                	public String showSelectStyles(Model model, @ModelAttribute("commonBean") selectionBean commonBean){
                
                        /* common bean is fine in here. */
                }
                
                @ResourceMapping(value="showStyleDetails")
                public String showStyleDetails(Model model, 
                       @ModelAttribute("commonBean") selectionBean commonBean) {
                
                       /* In here if last request was action or other resource request,
                                commonBean is initialized to the value at the beginning of 
                                session. */
                       /* However, if the last request was an event, then, the commmonBean
                                is reinstantiated, rather than taking it from the Session. */
                
                       . . . 
                		return("selectStyles.jsp");
                    }
                With the configuration:
                Code:
                <beans  . . . >
                        . . . .
                	<context:component-scan base-package="com.generic.orderentry.skuselection.requesthandler" />
                </beans>
                This was for code organization, since one large class was getting unwieldy. During initial testing w/ just resource and action requests, everything worked as expected. Only when the event handling got tested, did the exact same showSelectStyles() get a newly instantiated commonBean rather than the one previously added to the session. What's confusing is that it wasn't on the event request, but on the subsequent resource request after the event request had been handled that the problem appeared. The same resource request received the correct value of commonBean when it was just action & resource requests.

                The resolution was to add
                Code:
                @SessionAttributes(value={"commonBean"})
                to the 2nd file. Then, it worked.

                I don't get this. I'm inclined to think this is a bug, but a pretty obscure one. I'll see what I can come up with to boil this down into the smallest example possible and file a bug report.

                Thanks for the help.

                Comment


                • #9
                  Ah, ok. Let me check I understand this correctly. You have two controllers and you want to share @SessionAttributes across both? Then, yes, you need the annotation on both controllers.

                  I don't believe this is a bug. That annotation causes the value to be copied from the underlying session into the implicit model. If you don't have the annotation on the second controller, the model won't be populated from the value in session (even though the value IS there) but a new instance will be created.

                  Also see the comments in the Javadoc about storing temporarily vs permanently.
                  http://static.springsource.org/sprin...ttributes.html

                  Comment

                  Working...
                  X