DWR

AbstractContainer.callInitializingBeans instantiates beans regardless of their spring scope to check instanceof InitializingBean

Details

  • Type: Bug Bug
  • Status: Resolved Resolved
  • Priority: Critical Critical
  • Resolution: Fixed
  • Affects Version/s: 2.0.rc1, 2.0.rc2, 2.0.rc3, 2.0.rc4, 2.0.rc5, 2.0, 2.0.1, 2.0.2, 2.0.3, 2.0.4, 2.0.5, 2.0.6, 2.0.7, 2.0.8, 2.0.9, 3.0.M1
  • Fix Version/s: 2.0.10
  • Component/s: Spring
  • Documentation Required:
    No
  • Description:
    Hide
    AbstractContainer.callInitializingBeans attempts to create instances of all known bean names, regardless of their spring scope.

    This can cause session scoped beans to get the initialized before their dependencies can be resolved and their requirements such as the user being authenticated are met.

    The stack is as follows during the very first access to the webapp:

    MyUserSessionScopedBean.initialize() line: 94
    LifeCyclePostBeanPostProcessor.postProcessAfterInitialization(Object, String) line: 30
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyBeanPostProcessorsAfterInitialization(Object, String) line: 361
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1344
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 473
    AbstractAutowireCapableBeanFactory$1.run() line: 409
    AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 380
    AbstractBeanFactory$2.getObject() line: 302
    UserSessionScope.get(String, ObjectFactory) line: 51
    DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class, Object[], boolean) line: 298
    DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 185
    DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 164
    ...
    SpringContainer.getBean(String) line: 85
    SpringContainer(AbstractContainer).callInitializingBeans() line: 40
    SpringContainer(DefaultContainer).setupFinished() line: 181
    DwrSpringServlet.init(ServletConfig) line: 95

    -----------------

    I've verified that this problem exists for both the 2.0.X and 3.0.X code trees. It either causes session scoped beans to throw exceptions due to being instantiated too early, or will leave them in an invalid state. Bottom line is that DWR shouldn't attempt to instantiate them, and that this bug can prevent the spring context beans from being properly set up.

    A possible solution is to use ListableBeanFactory.getBeansOfType(InitializingBean.class) instead of the current callInitializingBeans implementation which uses instanceof InitializingBean.
    Show
    AbstractContainer.callInitializingBeans attempts to create instances of all known bean names, regardless of their spring scope. This can cause session scoped beans to get the initialized before their dependencies can be resolved and their requirements such as the user being authenticated are met. The stack is as follows during the very first access to the webapp: MyUserSessionScopedBean.initialize() line: 94 LifeCyclePostBeanPostProcessor.postProcessAfterInitialization(Object, String) line: 30 DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyBeanPostProcessorsAfterInitialization(Object, String) line: 361 DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1344 DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 473 AbstractAutowireCapableBeanFactory$1.run() line: 409 AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method] DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 380 AbstractBeanFactory$2.getObject() line: 302 UserSessionScope.get(String, ObjectFactory) line: 51 DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class, Object[], boolean) line: 298 DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 185 DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 164 ... SpringContainer.getBean(String) line: 85 SpringContainer(AbstractContainer).callInitializingBeans() line: 40 SpringContainer(DefaultContainer).setupFinished() line: 181 DwrSpringServlet.init(ServletConfig) line: 95 ----------------- I've verified that this problem exists for both the 2.0.X and 3.0.X code trees. It either causes session scoped beans to throw exceptions due to being instantiated too early, or will leave them in an invalid state. Bottom line is that DWR shouldn't attempt to instantiate them, and that this bug can prevent the spring context beans from being properly set up. A possible solution is to use ListableBeanFactory.getBeansOfType(InitializingBean.class) instead of the current callInitializingBeans implementation which uses instanceof InitializingBean.
  1. dwr-spring-test.zip
    (3.42 MB)
    Jim Meyer
    31/Jan/12 5:57 AM
  1. build-82.jpg
    (463 kB)
  2. reproduce-eclipse-dwr-3.0.M1.jpg
    (458 kB)

Issue Links

Activity

Hide
David Marginian added a comment - 31/Jan/12 5:00 AM

Jim, I am not a Spring expert but from my understanding shouldn't you be using an aop:scoped-proxy to alleviate this issue?

http://static.springsource.org/spring/docs/2.0.x/reference/beans.html (3.4.4.5. Scoped beans as dependencies).

We also briefly mention this in our Spring docs:
http://directwebremoting.org/dwr/documentation/server/integration/spring.html (Scoped Beans).

Show
David Marginian added a comment - 31/Jan/12 5:00 AM Jim, I am not a Spring expert but from my understanding shouldn't you be using an aop:scoped-proxy to alleviate this issue? http://static.springsource.org/spring/docs/2.0.x/reference/beans.html (3.4.4.5. Scoped beans as dependencies). We also briefly mention this in our Spring docs: http://directwebremoting.org/dwr/documentation/server/integration/spring.html (Scoped Beans).
Hide
Jim Meyer added a comment - 31/Jan/12 5:57 AM

I've added a test case that shows the problem.

Run with maven jetty:run and access index.html. Set a break point in ScopedBean.afterPropertiesSet to see the problem reproduced. Note this only happens once when the web app has just been started.

So the main issue is that beans that have nothing to do with dwr are forced into existance, which can only be considered a bug, especially in big web apps where dwr:remote is only used on a few beans.

Hope this clears things up.

Show
Jim Meyer added a comment - 31/Jan/12 5:57 AM I've added a test case that shows the problem. Run with maven jetty:run and access index.html. Set a break point in ScopedBean.afterPropertiesSet to see the problem reproduced. Note this only happens once when the web app has just been started. So the main issue is that beans that have nothing to do with dwr are forced into existance, which can only be considered a bug, especially in big web apps where dwr:remote is only used on a few beans. Hope this clears things up.
Hide
David Marginian added a comment - 31/Jan/12 6:06 AM

Jim, that helps a lot. I will take a look at this either this week or this weekend. Thank you for taking the time to provide more details and create the test case.

Show
David Marginian added a comment - 31/Jan/12 6:06 AM Jim, that helps a lot. I will take a look at this either this week or this weekend. Thank you for taking the time to provide more details and create the test case.
Hide
Jim Meyer added a comment - 31/Jan/12 6:16 AM

No problem.

The solution I mentioned in the last line of the issue, if applicable, would also speed up the time it takes to initialize the DWR Spring servlet for large web apps.

Thanks,
Jim

Show
Jim Meyer added a comment - 31/Jan/12 6:16 AM No problem. The solution I mentioned in the last line of the issue, if applicable, would also speed up the time it takes to initialize the DWR Spring servlet for large web apps. Thanks, Jim
Hide
David Marginian added a comment - 02/Feb/12 5:48 AM

Jim, I stepped through this, this morning using the latest DWR. Here is what is happening (a bit different than what you suspected):

Show
David Marginian added a comment - 02/Feb/12 5:48 AM Jim, I stepped through this, this morning using the latest DWR. Here is what is happening (a bit different than what you suspected):
Hide
David Marginian added a comment - 02/Feb/12 5:52 AM

Sorry, accidentally saved that last comment. Picking up where I left off:

AbstractContainer.callInitializingBeans

The instanceof check here is not the problem. The bean is being retrieved/instantiated from the Spring Container in URLProcessor.afterContainerSetup where the beans are retrieved from the container and getBean is called. I don't have a solution for this yet just logging my initial findings here.

Show
David Marginian added a comment - 02/Feb/12 5:52 AM Sorry, accidentally saved that last comment. Picking up where I left off: AbstractContainer.callInitializingBeans The instanceof check here is not the problem. The bean is being retrieved/instantiated from the Spring Container in URLProcessor.afterContainerSetup where the beans are retrieved from the container and getBean is called. I don't have a solution for this yet just logging my initial findings here.
Hide
Jim Meyer added a comment - 02/Feb/12 5:56 AM

Agreed that it's not instanceof that's the problem, but it's the reason why getBeans on all bean names is called a few lines before

From what I understand, using ListableBeanFactory.getBeansOfType would be a better and more effective way, and would fix this issue.

Show
Jim Meyer added a comment - 02/Feb/12 5:56 AM Agreed that it's not instanceof that's the problem, but it's the reason why getBeans on all bean names is called a few lines before From what I understand, using ListableBeanFactory.getBeansOfType would be a better and more effective way, and would fix this issue.
Hide
David Marginian added a comment - 02/Feb/12 6:05 AM

Actually, I was incorrect there is a check in place where I mentioned, so get Bean isn't called there either.

You are incorrect, for what I see callInitializing beans only gets called with DWR's internal beans. You can verify this by placing a breakpoint there.

I don't even see the breakpoint in ScopedBean being hit (once again I am using DWR trunk). Have you tried using trunk?

Show
David Marginian added a comment - 02/Feb/12 6:05 AM Actually, I was incorrect there is a check in place where I mentioned, so get Bean isn't called there either. You are incorrect, for what I see callInitializing beans only gets called with DWR's internal beans. You can verify this by placing a breakpoint there. I don't even see the breakpoint in ScopedBean being hit (once again I am using DWR trunk). Have you tried using trunk?
Hide
Jim Meyer added a comment - 02/Feb/12 6:20 AM

Hmm.. I just ran the test and hit the break point. I'm using jetty:run as mvn target in eclipse. I see that the test case POM refers to 2.0.3 of DWR, so you should at least be able to reproduce it with that version.

Is there a trunk/snapshot version I can add to the test case POM to run against the latest DWR?

Show
Jim Meyer added a comment - 02/Feb/12 6:20 AM Hmm.. I just ran the test and hit the break point. I'm using jetty:run as mvn target in eclipse. I see that the test case POM refers to 2.0.3 of DWR, so you should at least be able to reproduce it with that version. Is there a trunk/snapshot version I can add to the test case POM to run against the latest DWR?
Hide
Jim Meyer added a comment - 02/Feb/12 6:26 AM

I just checked trunk SpringContainer and it has the following getBeanNames method, which doesn't filter to just dwr beans. In other words, it should return the ScopedBean instance along with all other beans in the spring context:

/* (non-Javadoc)

  • @see org.directwebremoting.impl.DefaultContainer#getBeanNames()
    */
    @Override
    public Collection<String> getBeanNames()
    {
    List<String> names = new ArrayList<String>();

// Snarf the beans from Spring
if (beanFactory instanceof ListableBeanFactory)
{
try

{ ListableBeanFactory listable = (ListableBeanFactory) beanFactory; names.addAll(Arrays.asList(listable.getBeanDefinitionNames())); }

catch (IllegalStateException ex)

{ log.warn("List of beanNames does not include Spring beans since the BeanFactory was closed when we tried to read it."); }

}
else

{ log.warn("List of beanNames does not include Spring beans since your BeanFactory is not a ListableBeanFactory."); }

// And append the DWR ones
names.addAll(super.getBeanNames());

return Collections.unmodifiableCollection(names);
}

Show
Jim Meyer added a comment - 02/Feb/12 6:26 AM I just checked trunk SpringContainer and it has the following getBeanNames method, which doesn't filter to just dwr beans. In other words, it should return the ScopedBean instance along with all other beans in the spring context: /* (non-Javadoc)
  • @see org.directwebremoting.impl.DefaultContainer#getBeanNames() */ @Override public Collection<String> getBeanNames() { List<String> names = new ArrayList<String>();
// Snarf the beans from Spring if (beanFactory instanceof ListableBeanFactory) { try { ListableBeanFactory listable = (ListableBeanFactory) beanFactory; names.addAll(Arrays.asList(listable.getBeanDefinitionNames())); } catch (IllegalStateException ex) { log.warn("List of beanNames does not include Spring beans since the BeanFactory was closed when we tried to read it."); } } else { log.warn("List of beanNames does not include Spring beans since your BeanFactory is not a ListableBeanFactory."); } // And append the DWR ones names.addAll(super.getBeanNames()); return Collections.unmodifiableCollection(names); }
Hide
David Marginian added a comment - 02/Feb/12 6:57 AM

I would like for you to try this using RC3-Snaphshot - you can find it in Sonatype's OSS repo - https://oss.sonatype.org/ or RC2. Note the Spring container code is the same as the RC2 release.

I started looking at this in RC2 because you indicated in the JIRA issue that this is an affected version (which does not appear to be the case).

All comments below relate to 3.x and up:
I understand that method returns all of the Bean Names (in your Spring context). I am unsure how we could distinguish a Bean from your context that you want to expose to DWR and one you do not (keep in mind there are many configuration options)? However, the callInitializingBeans in SpringContainer only passes in DWR internal beans to AbstractContainers.callInitializingBeans.

From SpringContainer:

/**

  • Avoids initialization of lazy-init beans in Spring context.
    */
    @Override
    protected void callInitializingBeans() { callInitializingBeans(super.getBeanNames()); }

The super will only retrieve DWR internal beans, so callIntializingBeans is not the problem as getBean is only being called on internal DWR beans (Beans from your Spring Context aren't in beanNames). NOTE: this has NOT always been the case! It was originally fixed on 5/29/08 for DWR-254.

So, first I would like you to try things out in RC2 or RC3-Snapshot. The problem should be fixed and if you can upgrade to that version than I will close this issue as a duplicate of DWR-254. If you cannot upgrade I will remove the affects RC2 from this issue, mark it for the next 2.x release and try to get a fix out to you ASAP (IF the fix is not too involved).

Show
David Marginian added a comment - 02/Feb/12 6:57 AM I would like for you to try this using RC3-Snaphshot - you can find it in Sonatype's OSS repo - https://oss.sonatype.org/ or RC2. Note the Spring container code is the same as the RC2 release. I started looking at this in RC2 because you indicated in the JIRA issue that this is an affected version (which does not appear to be the case). All comments below relate to 3.x and up: I understand that method returns all of the Bean Names (in your Spring context). I am unsure how we could distinguish a Bean from your context that you want to expose to DWR and one you do not (keep in mind there are many configuration options)? However, the callInitializingBeans in SpringContainer only passes in DWR internal beans to AbstractContainers.callInitializingBeans. From SpringContainer: /**
  • Avoids initialization of lazy-init beans in Spring context. */ @Override protected void callInitializingBeans() { callInitializingBeans(super.getBeanNames()); }
The super will only retrieve DWR internal beans, so callIntializingBeans is not the problem as getBean is only being called on internal DWR beans (Beans from your Spring Context aren't in beanNames). NOTE: this has NOT always been the case! It was originally fixed on 5/29/08 for DWR-254. So, first I would like you to try things out in RC2 or RC3-Snapshot. The problem should be fixed and if you can upgrade to that version than I will close this issue as a duplicate of DWR-254. If you cannot upgrade I will remove the affects RC2 from this issue, mark it for the next 2.x release and try to get a fix out to you ASAP (IF the fix is not too involved).
Hide
David Marginian added a comment - 02/Feb/12 7:05 AM

Jim, do not use M1. That is not a valid test either. Please use the version from the Sonatype OSS repo OR RC2 from our site (add into your local Maven repo).

Sorry for the confusion. This is due to the fact that we don't build in Maven and in the past have had difficulties getting our artifacts into Maven central. There used to be a manual process for this but it has changed over the years and there was a long period of time where we didn't release artifacts into Maven central because it was simply too difficult. Those issues have been resolved and all releases moving forward will be in Maven central.

Show
David Marginian added a comment - 02/Feb/12 7:05 AM Jim, do not use M1. That is not a valid test either. Please use the version from the Sonatype OSS repo OR RC2 from our site (add into your local Maven repo). Sorry for the confusion. This is due to the fact that we don't build in Maven and in the past have had difficulties getting our artifacts into Maven central. There used to be a manual process for this but it has changed over the years and there was a long period of time where we didn't release artifacts into Maven central because it was simply too difficult. Those issues have been resolved and all releases moving forward will be in Maven central.
Hide
David Marginian added a comment - 02/Feb/12 7:05 AM

As you can see http://bugs.directwebremoting.org/jira/browse/DWR-254 is listed as affecting M1. The fix was after M1 was released.

Show
David Marginian added a comment - 02/Feb/12 7:05 AM As you can see http://bugs.directwebremoting.org/jira/browse/DWR-254 is listed as affecting M1. The fix was after M1 was released.
Hide
David Marginian added a comment - 02/Feb/12 7:17 AM

The fix is the same as the fix for DWR-254, essentially making sure callInitializingBeans is called ONLY with DWR internal beans. This can be accomplished by passing super.getBeansNames() into the callInitialzingBeans call.

Show
David Marginian added a comment - 02/Feb/12 7:17 AM The fix is the same as the fix for DWR-254, essentially making sure callInitializingBeans is called ONLY with DWR internal beans. This can be accomplished by passing super.getBeansNames() into the callInitialzingBeans call.
Hide
Jim Meyer added a comment - 02/Feb/12 7:24 AM

Confirmed that it's fixed in 3.0-RC2, but affects M1 and before as you now indicate in the affects versions.

Would be great if the fix is added to 2.0.X since this would be a smaller upgrade for us.

Thanks for your help David. Appreciate it.

Show
Jim Meyer added a comment - 02/Feb/12 7:24 AM Confirmed that it's fixed in 3.0-RC2, but affects M1 and before as you now indicate in the affects versions. Would be great if the fix is added to 2.0.X since this would be a smaller upgrade for us. Thanks for your help David. Appreciate it.
Hide
David Marginian added a comment - 02/Feb/12 7:27 AM

I think the fix will be very quick for 2.x. Can you help test it out for me when I get the fix in?

Show
David Marginian added a comment - 02/Feb/12 7:27 AM I think the fix will be very quick for 2.x. Can you help test it out for me when I get the fix in?
Hide
David Marginian added a comment - 02/Feb/12 7:59 AM

Ok, I made a change. It is on our CI build server - http://ci.directwebremoting.org/bamboo/browse/DWR20-ALL-82/artifact.

Show
David Marginian added a comment - 02/Feb/12 7:59 AM Ok, I made a change. It is on our CI build server - http://ci.directwebremoting.org/bamboo/browse/DWR20-ALL-82/artifact.
Hide
Jim Meyer added a comment - 03/Feb/12 1:05 AM

Hi David.

Added the jar at http://ci.directwebremoting.org/bamboo/browse/DWR20-ALL-82/artifact/JOB1/dwr.jar/dwr.jar to local rep and ran the test case (which doesn't fail with 3.0-RC2) but it still fails.

I see that there's changes in AbstractContainer but they don't seem to have done the trick.

Show
Jim Meyer added a comment - 03/Feb/12 1:05 AM Hi David. Added the jar at http://ci.directwebremoting.org/bamboo/browse/DWR20-ALL-82/artifact/JOB1/dwr.jar/dwr.jar to local rep and ran the test case (which doesn't fail with 3.0-RC2) but it still fails. I see that there's changes in AbstractContainer but they don't seem to have done the trick.
Hide
David Marginian added a comment - 03/Feb/12 4:43 AM

Sorry Jim. Try again - http://ci.directwebremoting.org/bamboo/browse/DWR20-ALL-84/artifact. This one should do the trick (had time to test it).

Show
David Marginian added a comment - 03/Feb/12 4:43 AM Sorry Jim. Try again - http://ci.directwebremoting.org/bamboo/browse/DWR20-ALL-84/artifact. This one should do the trick (had time to test it).
Hide
Jim Meyer added a comment - 03/Feb/12 5:21 AM

Build 84 did the trick. ScopedBean is no longer one of the bean names in AbstractContainer.callInitializingBeans.

Show
Jim Meyer added a comment - 03/Feb/12 5:21 AM Build 84 did the trick. ScopedBean is no longer one of the bean names in AbstractContainer.callInitializingBeans.
Hide
David Marginian added a comment - 03/Feb/12 5:26 AM

Thanks Jim. I will try to release 2.0.10 in the next week or so.

Show
David Marginian added a comment - 03/Feb/12 5:26 AM Thanks Jim. I will try to release 2.0.10 in the next week or so.
Hide
David Marginian added a comment - 07/Feb/12 6:31 PM

I just pushed out 2.0.10 with this fix. It will be in Maven central in a few hours. I will update the site in the coming days.

Show
David Marginian added a comment - 07/Feb/12 6:31 PM I just pushed out 2.0.10 with this fix. It will be in Maven central in a few hours. I will update the site in the coming days.

People

Dates

  • Created:
    31/Jan/12 3:13 AM
    Updated:
    07/Feb/12 6:31 PM
    Resolved:
    07/Feb/12 6:31 PM