gnodet commented on issue #13827: URL: https://github.com/apache/maven/issues/13827#issuecomment-6639331548
The root cause here is that `MojoExecutionScope` has always been `ThreadLocal`-based (since MNG-5530 in 2012), and that is intentional: when running with `-T N`, multiple mojo executions run concurrently on different threads, and the `ThreadLocal` isolates their scoped state. ## Maven 3.9.x Option 2 (documentation + guidance) is the right answer for 3.x. The workaround in apache/maven-site-plugin#1350 — routing document rendering back to the mojo thread via a queue — is the correct pattern. Plugins that spawn worker threads must not let those threads look up `@MojoExecutionScoped` components, because there is no scope state on those threads. This should be documented clearly. ## Maven 4 The situation is cleaner by design: **A v4 plugin** gets a fresh `Injector` per mojo execution (created in `DefaultMavenPluginManager`), with `Session`, `Project`, `MojoExecution`, and `Log` bound directly as plain instances via `bindInstance`. There is no `ThreadLocal` involved at all. A mojo simply declares: ```java @Inject Session session; @Inject Project project; @Inject MojoExecution mojoExecution; ``` ...and gets the right instances for that execution automatically. If the mojo spawns worker threads, it passes those objects explicitly as parameters — no scope, no magic. v4 mojos are thread-safe by design: each execution gets a fresh mojo instance with its own injector, so no mojo state is ever shared between concurrent executions. This is what makes v4 mojos safe to run in parallel builds without locking. If a plugin needs to share state across multiple mojo invocations (e.g. an `initialize` mojo and a `verify` mojo sharing computed data), it can inject a `@SessionScoped` or `@Singleton` bean — those are resolved from the shared container, not the per-execution local injector, so they survive across executions for the duration of the build. Those shared beans are the only place where explicit thread-safety is required. **A core extension** (`.mvn/extensions.xml`) lives in the shared container and can use `@SessionScoped` for session-lifetime components. The existing `@MojoExecutionScoped` (v3 annotation, Plexus/Sisu/Guice path) still works for extensions that need per-execution lifecycle — but even then, the `ThreadLocal` constraint applies: the scope is only valid on the mojo thread. ## On `org.apache.maven.api.di.MojoExecutionScoped` (the v4 annotation) This annotation exists in `maven-api-di` but is unfinished and unusable: the Guice binding in `MojoExecutionScopeModule` is commented out, there is no proxy path for injecting it into a wider scope (unlike `@SessionScoped` which creates a JDK proxy transparently), and no production code in Maven itself uses it. We should remove it from the v4 API to avoid confusion — v4 plugins do not need it (they get execution context via direct injection), and extensions that need scoped components should use the established v3 annotation or `@SessionScoped`. ## On Option 1 (context propagation API) Not justified. It would work around a design problem rather than fixing it. Plugins that need concurrent work should pass context explicitly, not rely on transparent scope propagation across threads. ## Long-term for `site:run` Migrating `maven-site-plugin` to the v4 API is the right fix. Doxia parsers would receive `Session` and `Project` via direct injection or explicit parameter passing — no scoped lookups, no `ThreadLocal`, no thread issues. -- 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]
