[
https://issues.apache.org/jira/browse/WW-4291?focusedWorklogId=1002871&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1002871
]
ASF GitHub Bot logged work on WW-4291:
--------------------------------------
Author: ASF GitHub Bot
Created on: 01/Feb/26 09:24
Start Date: 01/Feb/26 09:24
Worklog Time Spent: 10m
Work Description: lukaszlenart opened a new pull request, #1562:
URL: https://github.com/apache/struts/pull/1562
## Summary
This PR implements two-phase processing for conversion properties to enable
Spring bean name resolution in `struts-conversion.properties` files.
**Problem:** Users could not reference Spring bean names in conversion
properties. When specifying a bean name (e.g., "myConverter") instead of a
fully qualified class name, a `ClassNotFoundException` was thrown.
**Root Cause:** Type converters were processed during the bootstrap phase
before `SpringObjectFactory` was available.
`SpringObjectFactory.getClassInstance()` already supports bean names via
`containsBean()` check, but it wasn't being used during early initialization.
**Solution:** Split conversion property processing into two phases:
- **Early phase (EarlyInitializable):** Process
`struts-default-conversion.properties` only - contains class names
- **Late phase (Initializable):** Process user properties
(`struts-conversion.properties`, `xwork-conversion.properties`) when
`SpringObjectFactory` is available
## Changes
- Add `UserConversionPropertiesProvider` interface defining the late
initialization contract
- Add `UserConversionPropertiesProcessor` implementing `Initializable` for
late-phase processing
- Modify `StrutsConversionPropertiesProcessor` to implement
`UserConversionPropertiesProvider` and split `init()` into early/late phases
- Register new beans in `DefaultConfiguration` and `struts-beans.xml`
- Add alias in `StrutsBeanSelectionProvider` for proper dependency injection
- Explicitly trigger late initialization in
`DefaultConfiguration.reloadContainer()`
- Improve JavaDocs for `BeanSelectionProvider` and
`AbstractBeanSelectionProvider`
## Usage After This Change
Users can now specify Spring bean names in `struts-conversion.properties`:
```properties
# Using Spring bean name
java.time.LocalDate=localDateConverter
# Using class name (still works - backward compatible)
java.util.UUID=com.example.UUIDConverter
```
## Test Plan
- [x] Unit tests for `StrutsConversionPropertiesProcessor` two-phase
processing
- [x] Integration tests for Spring bean name resolution via
`SpringObjectFactory`
- [ ] Run full test suite: `mvn test -DskipAssembly`
- [ ] Manual verification with sample Spring application
Fixes [WW-4291](https://issues.apache.org/jira/browse/WW-4291)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Issue Time Tracking
-------------------
Worklog Id: (was: 1002871)
Remaining Estimate: 0h
Time Spent: 10m
> Can't use Spring bean name for type convertor
> ---------------------------------------------
>
> Key: WW-4291
> URL: https://issues.apache.org/jira/browse/WW-4291
> Project: Struts 2
> Issue Type: Improvement
> Components: Plugin - Spring
> Affects Versions: 6.1.1
> Reporter: Jasper Rosenberg
> Assignee: Lukasz Lenart
> Priority: Minor
> Fix For: 6.9.0, 7.2.0
>
> Time Spent: 10m
> Remaining Estimate: 0h
>
> If in your xwork.conversion.properties file you try to use a Spring bean name
> instead of a class name, it blows up.
> This is because DefaultConfiguration.createBootstrapContainer() ends up using
> DefaultTypeConverterCreator which has the generic ObjectFactory at that point
> because it happens before the struts.properties file is ever loaded (where in
> my case the SpringObjectFactory is defined.)
> {noformat}
> 10:20:06,910 ERROR [DefaultConversionPropertiesProcessor] Conversion
> registration error
> java.lang.ClassNotFoundException: entityObjectTypeConvertor
> at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1680)
> at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526)
> at
> com.opensymphony.xwork2.util.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:152)
> at
> com.opensymphony.xwork2.ObjectFactory.getClassInstance(ObjectFactory.java:108)
> at
> com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:161)
> at
> com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:151)
> at
> com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator.createTypeConverter(DefaultTypeConverterCreator.java:23)
> at
> com.opensymphony.xwork2.conversion.impl.DefaultConversionPropertiesProcessor.loadConversionProperties(DefaultConversionPropertiesProcessor.java:64)
> at
> com.opensymphony.xwork2.conversion.impl.DefaultConversionPropertiesProcessor.process(DefaultConversionPropertiesProcessor.java:40)
> at
> com.opensymphony.xwork2.conversion.impl.XWorkConverter.setConversionPropertiesProcessor(XWorkConverter.java:179)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> at java.lang.reflect.Method.invoke(Method.java:606)
> at
> com.opensymphony.xwork2.inject.ContainerImpl$MethodInjector.inject(ContainerImpl.java:299)
> at
> com.opensymphony.xwork2.inject.ContainerImpl$ConstructorInjector.construct(ContainerImpl.java:438)
> at
> com.opensymphony.xwork2.inject.ContainerBuilder$5.create(ContainerBuilder.java:207)
> ...
> at
> com.opensymphony.xwork2.inject.ContainerBuilder$7.call(ContainerBuilder.java:484)
> at
> com.opensymphony.xwork2.inject.ContainerImpl.callInContext(ContainerImpl.java:584)
> at
> com.opensymphony.xwork2.inject.ContainerBuilder.create(ContainerBuilder.java:484)
> at
> com.opensymphony.xwork2.config.impl.DefaultConfiguration.createBootstrapContainer(DefaultConfiguration.java:324)
> at
> com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContainer(DefaultConfiguration.java:221)
> at
> com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:67)
> at
> org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:446)
> at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:490)
> at
> org.apache.struts2.dispatcher.ng.InitOperations.initDispatcher(InitOperations.java:74)
> at
> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.init(StrutsPrepareAndExecuteFilter.java:57)
> at
> org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:325)
> at
> org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:235)
> at
> org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:194)
> at
> org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:295)
> at
> org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:422)
> at
> org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:115)
> at
> org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4072)
> at
> org.apache.catalina.core.StandardContext.start(StandardContext.java:4726)
> at
> org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:799)
> at
> org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:779)
> at
> org.apache.catalina.core.StandardHost.addChild(StandardHost.java:601)
> at
> org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:943)
> at
> org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:778)
> at
> org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:504)
> at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1317)
> at
> org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:324)
> at
> org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142)
> at
> org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1065)
> at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
> at
> org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
> at
> org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
> at
> org.apache.catalina.core.StandardService.start(StandardService.java:525)
> at
> org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
> at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> at java.lang.reflect.Method.invoke(Method.java:606)
> at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
> at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
> 10:20:07,074 INFO [XmlConfigurationProvider] Parsing configuration file
> [struts-default.xml]
> 10:20:07,159 INFO [XmlConfigurationProvider] Parsing configuration file
> [struts-plugin.xml]
> 10:20:07,663 INFO [XmlConfigurationProvider] Parsing configuration file
> [struts.xml]
> 10:20:07,674 INFO [BeanSelectionProvider] Choosing bean (struts) for
> (com.opensymphony.xwork2.FileManagerFactory)
> {noformat}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)