Cache projectsUsingPlugin list to avoid O(N²) reactor scan 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 <noreply@anthropic.com>
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 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 @@ 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<Project> getProjectsUsingPlugin() { + List<Project> allProjects = session.getProjects(); + if (allProjects.isEmpty()) { + return List.of(); + } + Map<String, Object> ctx = session.getPluginContext(allProjects.get(0)); + List<Project> cached = (List<Project>) 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 @@ } } - List<Project> projectsUsingPlugin = - session.getProjects().stream().filter(this::usingPlugin).collect(Collectors.toList()); + List<Project> projectsUsingPlugin = getProjectsUsingPlugin(); if (allProjectsMarked(projectsUsingPlugin)) { for (Project reactorProject : projectsUsingPlugin) { State state = getState(reactorProject);