Open GoogleCodeExporter opened 9 years ago
OK, I'm attaching a patch for the issue - it can be applied to the test cased
posted with the issue to make it work.
The problem
===========
The problem is as follows. When binding a handler (via bindHandler in
HandlerModule) a bean is registered for each pair of Handler-Action. The class
of these beans is `ActionHandlerValidatorMapImpl`. By default spring gives a
bean an id equal to its class name, so having more than one Handler-Action pair
results in a collision of the generated id. When the spring context is run from
a web app the SpringUtils class takes care of changing the bean name:
public static <B> void registerBean(ApplicationContext applicationContext,
B instance) throws BeansException {
if (applicationContext instanceof GenericApplicationContext) {
(...)
} else if (applicationContext instanceof AbstractRefreshableWebApplicationContext) {
ConfigurableListableBeanFactory beanFactory = ((AbstractRefreshableWebApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerSingleton(*generateName(beanFactory, createBeanDefinition(instance))*, instance);
}
}
private static String generateName(ConfigurableListableBeanFactory registry,
RootBeanDefinition definition) {
(...)
String id = generatedBeanName;
// Top-level bean: use plain class name.
// Increase counter until the id is unique.
int counter = -1;
while (counter == -1 || (registry.containsSingleton(id))) {
counter++;
id = generatedBeanName + "#" + counter;
}
return id;
}
Unfortunately, this does not apply to GenericApplicationContext.
A solution
==========
A solution is to add a counter value to both beans registered in
`GenericApplicationContext` and `AbstractRefreshableWebApplicationContext`.
I've created a class for this very reason:
public enum GwtpBeanNameGenerator {
INSTANCE;
private static AtomicInteger counter = new AtomicInteger(0);
public String generateBeanName(BeanDefinition definition,
BeanFactory factory) {
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
} else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName()
+ "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException(
"Unnamed bean definition specifies neither "
+ "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
return generatedBeanName + "#" + counter.getAndIncrement();
}
}
It can be used as follows (in SpringUtils):
public static <B> void registerBean(ApplicationContext applicationContext,
B instance) throws BeansException {
if (applicationContext instanceof GenericApplicationContext) {
DefaultListableBeanFactory beanFactory = ((GenericApplicationContext) applicationContext)
.getDefaultListableBeanFactory();
beanFactory.registerSingleton(GwtpBeanNameGenerator.INSTANCE
.generateBeanName(createBeanDefinition(instance),
beanFactory), instance);
} else if (applicationContext instanceof AbstractRefreshableWebApplicationContext) {
ConfigurableListableBeanFactory beanFactory = ((AbstractRefreshableWebApplicationContext) applicationContext)
.getBeanFactory();
beanFactory.registerSingleton(GwtpBeanNameGenerator.INSTANCE
.generateBeanName(createBeanDefinition(instance),
beanFactory), instance);
}
}
For convenience I'm attaching both classes as well. I've tested in unit tests
and my two apps and it seems to work.
Hope it can help someone,
Igor Kupczynki
Original comment by puszczyk
on 11 May 2012 at 4:15
Attachments:
Thanks!
Before merging this, could you ask for a code review on codereview.appspot.com?
Original comment by goudreau...@gmail.com
on 23 May 2012 at 2:23
[deleted comment]
[deleted comment]
Hi, I've added the patch for a review:
https://codereview.appspot.com/6256073/
Thanks,
Igor
Original comment by puszczyk
on 30 May 2012 at 9:59
I've run into this issue as well. The root cause of the bug is that in
SpringUtils.registerBean() the beans are registered as singletons
(beanFactory.registerSingleton()) but for GenericApplicationContext the
DefaultBeanNameGenerator is used. The DefaultBeanNameGenerator looks for beans
in the registry as normal beans rather than singletons, so it doesn't find the
pre-existing bean and increment the suffix count.
The generateBeanName() method in SpringUtils looks like a copy of
BeanDefinitionReaderUtils.generateBeanName() but instead of doing the bean
lookup as a normal bean is looks them up as a singleton bean instead (as it
should since it registers them as singletons).
The fix should be to simply use SpringUtils.generateBeanName() for both the web
context and generic context. If you want to compare SpringUtils.generateName()
is here:
http://code.google.com/p/gwt-platform/source/browse/gwtp-core/gwtp-dispatch-serv
er-spring/src/main/java/com/gwtplatform/dispatch/server/spring/utils/SpringUtils
.java?r=27fb7e23931e91c46532fefb48c55a9163ebbdd6#84
And the DefaultBeanNameGenerator version (which just calls
BeanDefinitionReaderUtils.generateBeanName()) is here:
http://grepcode.com/file/repo1.maven.org/maven2/org.springframework/spring-beans
/3.0.5.RELEASE/org/springframework/beans/factory/support/BeanDefinitionReaderUti
ls.java#BeanDefinitionReaderUtils.generateBeanName%28org.springframework.beans.f
actory.config.BeanDefinition%2Corg.springframework.beans.factory.support.BeanDef
initionRegistry%2Cboolean%29
Have you found a good way to work around this issue or did you just patch your
GWTP install?
Original comment by jeraym...@gmail.com
on 30 Aug 2012 at 3:42
Original issue reported on code.google.com by
puszczyk
on 11 May 2012 at 9:29Attachments: