It is about time to post another answer to the 5 Spring pittfalls. So, here is the answer to the second fuzzy issue. Let's first recap the issue once again;
Fuzzy issue 2
Controller delegation is a powerful concept. In the code snippet below the tabController from WCB 1 delegates Spring controller methods to a subTabController from WCB 2. Controller delegation is required here to ensure that language labels coming from WCB 2 are displayed properly.
public class MyController extends ComponentController {
...
public void initializeTabSet(HttpServletRequest request, PanelBase panel)
MyTabController tabController = getStatefulTabController(MyTabController.class, request);
MySubTabController subTabController = getStatefulTabController(MySubTabController.class, request);
// Add a tab
addTab("bla", "panel.bla", "", "bla.jspf", myTabController, PanelTabset.LEVEL1_HORIZONTAL, request);
// Delegate controller methods to the subtab controller
tabController.addControllerDelegation(subTabController, fbo, request);
}
}
Question: what will happen here?
A. Each change in WCB 2 will only be effective after a restart of Tomcat. Updates will not effect the running WCB anymore.
B. The language labels of WCB 2 will be overruled by those of WCB 1.
C. This causes an endless recursion; the tabController invokes the subTabController and vice versa.
The answer
Well, the answer to this question is A. The WCBs seems to get sticked to that version, any update (even with uninstall) will simply not have any effect. So what is really going on here?
In fact, the answer has to be found in the OSGi framework we use (Felix), not Spring. You might even encountered this issue yourself many times with a WCB that had nothing to do with Spring. The "problem" is not really a problem, in fact it makes a lot of sense.
In the code snippet above the "addControllerDelegation" method adds the controller to an in-memory list of controllers of MyController to which it should delegate the Spring lifecyce methods. So WCB 1 keeps a reference to an object of which the class is contained by WCB 2. More important: it keeps holding that reference during the lifetime of the users' session since the controller is statefull.
In the OSGi framework each bundle (WCB) has its own classloader. The MyController class will loaded by the classloader of WCB 1 while the MySubTabController class will be loaded by the classloader of WCB 2. When WCB 2 is updated, usually the classloader of WCB 2 will be garbage collected and a new classloader will be instantiated that loads the new class definitions (note that classes themselves cannot be "unloaded"). However, this is not the case if someone still references this class. And that is exactly the case here!
That's why WCB 2 seems to be sticked to the same version. This will be the case as long as WCB 1 holds that reference. Note that in this particular case the problem will fix itself with a session timeout. In that case the MyController instance is garbage collected since the HTTP session that refers to it is garbage collected. After that, nobody holds a reference to objects of classes loaded by the classloader of WCB 2 and so an update of the WCB will succeed.
Ivo Ladage is product architect and is part of one of the SCRUM-teams. Ivo has special interests in Workflow and Authorization processes and Spring MVC.
Other blog entries: