DefaultPluginManagerTest.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.internal.impl;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.maven.api.Plugin;
import org.apache.maven.api.services.Lookup;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.internal.LifecyclePluginResolver;
import org.apache.maven.model.Build;
import org.apache.maven.plugin.MavenPluginManager;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.graph.DependencyNode;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
class DefaultPluginManagerTest {
@Test
void loadsPluginRuntimeForProjectDeclaration() throws Exception {
MavenPluginManager legacyPluginManager = mock(MavenPluginManager.class);
LifecyclePluginResolver lifecyclePluginResolver = mock(LifecyclePluginResolver.class);
Lookup lookup = mock(Lookup.class);
InternalMavenSession session = mock(InternalMavenSession.class);
MavenSession mavenSession = mock(MavenSession.class);
MavenSession executionSession = mock(MavenSession.class);
DefaultProject project = mock(DefaultProject.class);
MavenProject mavenProject = new MavenProject();
Build build = new Build();
org.apache.maven.model.Plugin model = new org.apache.maven.model.Plugin();
model.setGroupId("org.example");
model.setArtifactId("example-plugin");
model.setVersion("1.0");
build.addPlugin(model);
mavenProject.setBuild(build);
PluginDescriptor descriptor = mock(PluginDescriptor.class);
org.apache.maven.api.plugin.descriptor.PluginDescriptor apiDescriptor =
mock(org.apache.maven.api.plugin.descriptor.PluginDescriptor.class);
ClassRealm realm = mock(ClassRealm.class);
DependencyNode dependencyNode = mock(DependencyNode.class);
org.eclipse.aether.graph.Dependency resolverDependency = mock(org.eclipse.aether.graph.Dependency.class);
org.apache.maven.api.Dependency dependency = mock(org.apache.maven.api.Dependency.class);
when(session.getMavenSession()).thenReturn(mavenSession);
when(mavenSession.clone()).thenReturn(executionSession);
when(project.getProject()).thenReturn(mavenProject);
when(legacyPluginManager.getPluginDescriptor(eq(model), any(), any())).thenReturn(descriptor);
when(descriptor.getPluginDescriptorV4()).thenReturn(apiDescriptor);
when(descriptor.getClassRealm()).thenReturn(realm);
when(descriptor.getDependencyNode()).thenReturn(dependencyNode);
when(dependencyNode.getDependency()).thenReturn(resolverDependency);
when(dependencyNode.getChildren()).thenReturn(List.of());
when(session.getDependency(resolverDependency)).thenReturn(dependency);
when(dependency.getGroupId()).thenReturn("org.example");
when(dependency.getArtifactId()).thenReturn("plugin-spi");
DefaultPluginManager manager = new DefaultPluginManager(legacyPluginManager, lifecyclePluginResolver, lookup);
Plugin result = manager.loadPlugin(session, project, model.getDelegate());
assertSame(model.getDelegate(), result.getModel());
assertSame(apiDescriptor, result.getDescriptor());
assertSame(realm, result.getRealm().classLoader());
assertEquals(Optional.empty(), result.getRealm().moduleLayer());
assertEquals(List.of(dependency), result.getDependencies().stream().toList());
assertSame(dependency, result.getDependenciesMap().get("org.example:plugin-spi"));
verify(executionSession).setCurrentProject(mavenProject);
verify(lifecyclePluginResolver).resolveMissingPluginVersions(mavenProject, executionSession);
verify(legacyPluginManager).setupPluginRealm(descriptor, executionSession, null, null, null);
}
@Test
void preservesUndeclaredPluginError() throws Exception {
MavenPluginManager legacyPluginManager = mock(MavenPluginManager.class);
LifecyclePluginResolver lifecyclePluginResolver = mock(LifecyclePluginResolver.class);
InternalMavenSession session = mock(InternalMavenSession.class);
MavenSession mavenSession = mock(MavenSession.class);
MavenSession executionSession = mock(MavenSession.class);
DefaultProject project = mock(DefaultProject.class);
MavenProject mavenProject = new MavenProject();
Build build = new Build();
org.apache.maven.model.Plugin model = new org.apache.maven.model.Plugin();
model.setGroupId("org.example");
model.setArtifactId("missing-plugin");
mavenProject.setGroupId("org.example");
mavenProject.setArtifactId("example-project");
mavenProject.setVersion("1.0");
mavenProject.setBuild(build);
when(session.getMavenSession()).thenReturn(mavenSession);
when(mavenSession.clone()).thenReturn(executionSession);
when(project.getProject()).thenReturn(mavenProject);
DefaultPluginManager manager =
new DefaultPluginManager(legacyPluginManager, lifecyclePluginResolver, mock(Lookup.class));
IllegalArgumentException failure = assertThrows(
IllegalArgumentException.class, () -> manager.loadPlugin(session, project, model.getDelegate()));
assertEquals(
"Plugin org.example:missing-plugin is not declared by project " + mavenProject.getId(),
failure.getMessage());
verify(lifecyclePluginResolver).resolveMissingPluginVersions(mavenProject, executionSession);
verifyNoInteractions(legacyPluginManager);
}
@Test
void looksUpComponentsWithPluginClassLoaderAndRestoresOriginal() {
ClassLoader pluginClassLoader = new ClassLoader() {};
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
Object service = new Object();
Lookup lookup = mock(Lookup.class, invocation -> {
assertSame(pluginClassLoader, Thread.currentThread().getContextClassLoader());
return switch (invocation.getMethod().getName()) {
case "lookup" -> service;
case "lookupOptional" -> Optional.of(service);
case "lookupList" -> List.of(service);
case "lookupMap" -> Map.of("service", service);
default -> throw new AssertionError(invocation.getMethod());
};
});
DefaultRealm realm = new DefaultRealm(pluginClassLoader, lookup);
assertSame(service, realm.lookup().lookup(Object.class));
assertSame(service, realm.lookup().lookup(Object.class, "service"));
assertEquals(Optional.of(service), realm.lookup().lookupOptional(Object.class));
assertEquals(Optional.of(service), realm.lookup().lookupOptional(Object.class, "service"));
assertEquals(List.of(service), realm.lookup().lookupList(Object.class));
assertEquals(Map.of("service", service), realm.lookup().lookupMap(Object.class));
assertSame(originalClassLoader, Thread.currentThread().getContextClassLoader());
}
@Test
void restoresOriginalClassLoaderWhenLookupFails() {
Lookup lookup = mock(Lookup.class);
ClassLoader pluginClassLoader = new ClassLoader() {};
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
IllegalStateException failure = new IllegalStateException("boom");
doAnswer(invocation -> {
assertSame(pluginClassLoader, Thread.currentThread().getContextClassLoader());
throw failure;
})
.when(lookup)
.lookupList(Object.class);
DefaultRealm realm = new DefaultRealm(pluginClassLoader, lookup);
assertSame(
failure,
assertThrows(IllegalStateException.class, () -> realm.lookup().lookupList(Object.class)));
assertSame(originalClassLoader, Thread.currentThread().getContextClassLoader());
}
}