jdaugherty opened a new pull request, #16469: URL: https://github.com/apache/grails-core/pull/16469
## Problem Grails adds a `String -> Resource` converter to the conversion service that Spring Boot installs on both the environment and the bean factory. The bean factory consults that conversion service before its default property editors, and once `String -> Resource` exists, Spring's generic String, array and collection converters can reach `Resource[]` by converting one element at a time. A location pattern such as `classpath*:META-INF/foo.properties` therefore becomes a single `Resource` whose path is the literal pattern, instead of every matching resource. Spring's pattern-aware `ResourceArrayPropertyEditor` is never used. The conversion succeeds, because creating a `Resource` does not require it to exist, so the failure only shows up when the resource is opened: ``` Could not open ServletContext resource [/classpath*:META-INF/spring.integration.properties] ``` The converter dates from Grails 3.2, and the behavior is the same on Grails 7 / Boot 3. It became visible with Spring Boot 4: - Spring Integration registers an `integrationGlobalProperties` bean backed by a `PropertiesFactoryBean` whose `locations` is `classpath*:META-INF/spring.integration.properties`, an optional override that normally matches nothing. It only does so when no `integrationGlobalProperties` bean exists yet. - With Boot 3, `spring-boot-autoconfigure` shipped `IntegrationAutoConfiguration`, which defines that bean itself. Spring Integration's definition was skipped as a side effect, so its pattern never went through Grails' conversion. - Boot 4 moved that auto-configuration into `spring-boot-integration`. An application that depends on `spring-integration-core` without that module now gets Spring Integration's own definition, and startup fails as shown above. Adding `spring-boot-integration` avoids the Spring Integration case. However, any `Resource[]` bean property given a pattern is affected: a `PropertiesFactoryBean`, `locations` lists in `resources.groovy` or XML, library-defined beans, and `config.getProperty(name, Resource[])`. A controlled reproducer (a minimal `GrailsApp` plus `spring-integration-core` with `@EnableIntegration`) confirms this: | Grails / Boot | `integrationGlobalProperties` from | Result | |---|---|---| | 7.2.4 / 3.5 | Boot `IntegrationAutoConfiguration` | starts | | 7.2.4 / 3.5, no auto-configuration | Spring Integration | fails | | 8.0.0-RC1 / 4.1 | Spring Integration | fails | | 8.0.0-RC1 / 4.1 + `spring-boot-integration` | Boot `IntegrationAutoConfiguration` | starts | The conversion itself behaves the same on Spring 6.2.19/Boot 3.5.16 and Spring 7.0.9/Boot 4.1.1. ## Change - Register a `Resource[]` converter for `String`, `Object[]` and `Collection` sources. It delegates to Spring's `ResourceArrayPropertyEditor` with the application context and environment, so it keeps Spring's semantics: patterns expand to every match, or to an empty array when nothing matches, comma-delimited Strings hold several patterns, placeholders are resolved, and `Resource` elements are kept. Sources with other element types still fall through to the existing converters. `String -> Resource` is unchanged. - Register the converter set in one place, shared by `GrailsEarlyPluginRegistrationPostProcessor` and `GrailsApplicationPostProcessor`. Previously each registered its own copy, so the early phase, which plugin `doWithSpring` config reads run in, would have missed the array converter. - Upgrade guide: add the `Resource[]` behavior change, and note that Spring Integration auto-configuration now lives in `spring-boot-integration`. ## Tests `GrailsResourceConversionSpec` exercises: - a real `GrailsApp` start with application `doWithSpring` beans - the application lifecycle without the early phase - the early phase alone, with plugin `doWithSpring` beans and a config read It covers a Spring Integration-shaped unmatched `classpath*:` `PropertiesFactoryBean`, single patterns, comma lists, lists, arrays, placeholders, `config.getProperty(name, Resource[])`, and single `Resource` properties. Mutation-checked: - removing the array converter fails the whole spec - giving the early phase only the scalar converter fails the early-phase test - restricting the converter to `String` sources fails the list, array and lifecycle cases `:grails-core:test` (714 tests, 0 failures), `codeStyle` and `validateRepositoryConventions` pass locally. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
