From 05e6221587f26c87282769820e9d9088e2ff1301 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Mon, 3 Aug 2026 02:36:22 +0200 Subject: [PATCH] =?UTF-8?q?Cache=20projectsUsingPlugin=20list=20to=20avoid?= =?UTF-8?q?=20O(N=C2=B2)=20reactor=20scan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InstallMojo.execute() computes the list of reactor projects using the install plugin on every module invocation by streaming over all projects and calling getPluginsAsMap() for each one. In a 4383-module reactor, this produces ~19.2M filter evaluations (4383² calls). JFR profiling shows this as 5.6% of total CPU time, all in PluginContainer.getPluginsAsMap() called from usingPlugin(). Fix: cache the computed list in the first reactor project's plugin context on first invocation. The list is invariant during a build — which projects have the install plugin configured does not change between module invocations. This turns O(N²) into O(N). Co-Authored-By: Claude Opus 4.6 --- .../maven/plugins/install/InstallMojo.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/install/InstallMojo.java b/src/main/java/org/apache/maven/plugins/install/InstallMojo.java index d002520..f3183f5 100644 --- a/src/main/java/org/apache/maven/plugins/install/InstallMojo.java +++ b/src/main/java/org/apache/maven/plugins/install/InstallMojo.java @@ -98,6 +98,7 @@ private enum State { } private static final String INSTALL_PROCESSED_MARKER = InstallMojo.class.getName() + ".processed"; + private static final String PROJECTS_USING_PLUGIN_KEY = InstallMojo.class.getName() + ".projectsUsingPlugin"; public InstallMojo() {} @@ -120,6 +121,26 @@ private boolean hasState(Project project) { return pluginContext.containsKey(INSTALL_PROCESSED_MARKER); } + /** + * Returns the list of reactor projects that have this plugin configured, cached on first call. + * The list is invariant during a build and is stored in the current project's plugin + * context to avoid recomputing it on every module invocation (O(N) total instead of O(N²)). + */ + @SuppressWarnings("unchecked") + private List getProjectsUsingPlugin() { + List allProjects = session.getProjects(); + if (allProjects.isEmpty()) { + return List.of(); + } + Map ctx = session.getPluginContext(allProjects.get(0)); + List cached = (List) ctx.get(PROJECTS_USING_PLUGIN_KEY); + if (cached == null) { + cached = allProjects.stream().filter(this::usingPlugin).collect(Collectors.toList()); + ctx.put(PROJECTS_USING_PLUGIN_KEY, cached); + } + return cached; + } + private boolean usingPlugin(Project project) { Plugin plugin = project.getBuild().getPluginsAsMap().get("org.apache.maven.plugins:maven-install-plugin"); return plugin != null @@ -144,8 +165,7 @@ public void execute() { } } - List projectsUsingPlugin = - session.getProjects().stream().filter(this::usingPlugin).collect(Collectors.toList()); + List projectsUsingPlugin = getProjectsUsingPlugin(); if (allProjectsMarked(projectsUsingPlugin)) { for (Project reactorProject : projectsUsingPlugin) { State state = getState(reactorProject);