Skip to content

Load plugin.yml/plugin.groovy earlier so @Conditional* annotations can be used#15409

Draft
jdaugherty wants to merge 1 commit intoapache:7.1.xfrom
jdaugherty:conditionalPluginConfiguration
Draft

Load plugin.yml/plugin.groovy earlier so @Conditional* annotations can be used#15409
jdaugherty wants to merge 1 commit intoapache:7.1.xfrom
jdaugherty:conditionalPluginConfiguration

Conversation

@jdaugherty
Copy link
Contributor

Fixes #15408

Assisted-by: OpenCode <opencode@opencode.ai>
Assisted-by: Claude <claude@anthropic.com>

@jdaugherty jdaugherty force-pushed the conditionalPluginConfiguration branch from 1232204 to e109334 Compare February 18, 2026 16:53
@jdaugherty jdaugherty marked this pull request as draft February 18, 2026 16:57
@jdaugherty jdaugherty force-pushed the conditionalPluginConfiguration branch from 46f5e8f to 9f81969 Compare February 18, 2026 17:17
* @return the plugin's property source from the environment, or {@code null} if the
* GrailsApplication main context is not yet available or no matching property source exists
*/
@Deprecated(forRemoval = true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that if it's deprecated it should say why.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was proposing this be removed because I don't see a usage for it - you can fetch the configuration but when would it be valid to look at the shipped config? btw, i had marked this as draft to see what @matrei thinks of this. This is a rather large change for 7.0.x so we may want to do some further testing.

@bito-code-review
Copy link

You're right—deprecation annotations should include a reason for clarity. For this method, the reason could be that plugin property sources are now handled early by GrailsPluginEnvironmentPostProcessor.

@jamesfredley
Copy link
Contributor

If this does not require changes in end Grails apps I am OK with this going into 7.0.x, if we think the risk is too high, then 7.1.x is also OK.

Copy link
Contributor

@jamesfredley jamesfredley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks solid. A few minor items to take a look at.

The GrailsPluginConfigurationClass test removed the "plugin with both yml and groovy throws exception" test. That validation still exists in GrailsPluginEnvironmentPostProcessor.readPluginConfiguration() (line 293), but there's no unit test covering it in the new code. The GrailsPluginEnvironmentPostProcessorSpec should add a test for this.

* {@link org.grails.plugins.AbstractGrailsPlugin#readPluginConfiguration}. If both
* exist, a {@link RuntimeException} is thrown.</p>
*/
PluginInfo extractPluginInfo(Class<?> pluginClass) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calls pluginClass.getDeclaredConstructor().newInstance() to read loadAfter/loadBefore/dependsOn. At this point in the Spring Boot lifecycle, there's no GrailsApplication, no application context, no plugin manager. If any plugin's no-arg constructor has side effects or requires dependencies, this could fail.

The code handles this gracefully (catches exceptions, continues with default ordering), but plugins with complex constructors will silently get default ordering rather than their declared ordering. This could subtly change the order configs are loaded compared to the existing behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue at this point they shoudln't be using Holders either. These dependencies would have to be autowired and that would happen after the constructor creation, so i view this as low risk.

@jdaugherty jdaugherty force-pushed the conditionalPluginConfiguration branch from 462035b to ee32e61 Compare February 28, 2026 02:42
@jdaugherty jdaugherty changed the base branch from 7.0.x to 7.1.x March 1, 2026 05:35
@jdaugherty
Copy link
Contributor Author

This change is incomplete since the plugin configuration will be loaded potentially when the plugin is disabled. We may be able to post process already loaded configuration and remove them, but I'm not sure without further research.

Here's the loading order in Spring:

• SpringApplication.run(...)
• Prepare SpringApplicationRunListeners
• Prepare Environment
Create/attach ConfigurableEnvironment
Invoke all EnvironmentPostProcessors
• Create ApplicationContext
• Prepare context
• Refresh context (bean creation begins)

The problem is the plugin manager has different discovery mechanisms (but very similar). Worst, one of those mechanisms appears to require the application context, which won't exist when we need to load configuration. I think the need for the application context (or rather the parent application context) isn't actually used though.

I'm doing further research on this to see how best to unify the plugin manager mechanisms so we can share the logic in both places.

@jdaugherty
Copy link
Contributor Author

jdaugherty commented Mar 1, 2026

I've been working on this and will likely push a significant update in the next day or so. The core issue is the plugin filtering needs shared between the environment & the application post processors. The problem: the plugin manager is what currently does this filtering & it's a bean. The environment post processor can't use a bean b/c the context doesn't exist yet. The only practical place the filtering is used is when in unit tests it filters the plugins. I think the right solution is either moving the filter further out & sharing it between the plugin manager / EPP, or just moving the plugin manager away from being a bean completely.

@jdaugherty jdaugherty force-pushed the conditionalPluginConfiguration branch from ee32e61 to 406aa4a Compare March 2, 2026 00:15
@jdaugherty jdaugherty force-pushed the conditionalPluginConfiguration branch from 406aa4a to d7175cf Compare March 2, 2026 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Plugin configuration is loaded after @Conditional checks in Spring

3 participants