1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.cli.internal;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23
24 import java.io.File;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30
31 import org.apache.maven.RepositoryUtils;
32 import org.apache.maven.api.model.Plugin;
33 import org.apache.maven.cli.internal.extension.model.CoreExtension;
34 import org.apache.maven.execution.MavenExecutionRequest;
35 import org.apache.maven.extension.internal.CoreExports;
36 import org.apache.maven.extension.internal.CoreExtensionEntry;
37 import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
38 import org.apache.maven.plugin.PluginResolutionException;
39 import org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver;
40 import org.codehaus.plexus.DefaultPlexusContainer;
41 import org.codehaus.plexus.PlexusContainer;
42 import org.codehaus.plexus.classworlds.ClassWorld;
43 import org.codehaus.plexus.classworlds.realm.ClassRealm;
44 import org.codehaus.plexus.interpolation.InterpolationException;
45 import org.codehaus.plexus.interpolation.Interpolator;
46 import org.codehaus.plexus.interpolation.MapBasedValueSource;
47 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
48 import org.eclipse.aether.RepositorySystemSession;
49 import org.eclipse.aether.RepositorySystemSession.CloseableSession;
50 import org.eclipse.aether.artifact.Artifact;
51 import org.eclipse.aether.graph.DependencyFilter;
52 import org.eclipse.aether.repository.RemoteRepository;
53 import org.eclipse.aether.resolution.ArtifactResult;
54 import org.eclipse.aether.resolution.DependencyResult;
55 import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59
60
61
62 @Named
63 public class BootstrapCoreExtensionManager {
64 public static final String STRATEGY_PARENT_FIRST = "parent-first";
65 public static final String STRATEGY_PLUGIN = "plugin";
66 public static final String STRATEGY_SELF_FIRST = "self-first";
67
68 private final Logger log = LoggerFactory.getLogger(getClass());
69
70 private final DefaultPluginDependenciesResolver pluginDependenciesResolver;
71
72 private final DefaultRepositorySystemSessionFactory repositorySystemSessionFactory;
73
74 private final CoreExports coreExports;
75
76 private final ClassWorld classWorld;
77
78 private final ClassRealm parentRealm;
79
80 @Inject
81 public BootstrapCoreExtensionManager(
82 DefaultPluginDependenciesResolver pluginDependenciesResolver,
83 DefaultRepositorySystemSessionFactory repositorySystemSessionFactory,
84 CoreExports coreExports,
85 PlexusContainer container) {
86 this.pluginDependenciesResolver = pluginDependenciesResolver;
87 this.repositorySystemSessionFactory = repositorySystemSessionFactory;
88 this.coreExports = coreExports;
89 this.classWorld = ((DefaultPlexusContainer) container).getClassWorld();
90 this.parentRealm = container.getContainerRealm();
91 }
92
93 public List<CoreExtensionEntry> loadCoreExtensions(
94 MavenExecutionRequest request, Set<String> providedArtifacts, List<CoreExtension> extensions)
95 throws Exception {
96 try (CloseableSession repoSession = repositorySystemSessionFactory
97 .newRepositorySessionBuilder(request)
98 .build()) {
99 List<RemoteRepository> repositories = RepositoryUtils.toRepos(request.getPluginArtifactRepositories());
100 Interpolator interpolator = createInterpolator(request);
101
102 return resolveCoreExtensions(repoSession, repositories, providedArtifacts, extensions, interpolator);
103 }
104 }
105
106 private List<CoreExtensionEntry> resolveCoreExtensions(
107 RepositorySystemSession repoSession,
108 List<RemoteRepository> repositories,
109 Set<String> providedArtifacts,
110 List<CoreExtension> configuration,
111 Interpolator interpolator)
112 throws Exception {
113 List<CoreExtensionEntry> extensions = new ArrayList<>();
114
115 DependencyFilter dependencyFilter = new ExclusionsDependencyFilter(providedArtifacts);
116
117 for (CoreExtension extension : configuration) {
118 List<Artifact> artifacts =
119 resolveExtension(extension, repoSession, repositories, dependencyFilter, interpolator);
120 if (!artifacts.isEmpty()) {
121 extensions.add(createExtension(extension, artifacts));
122 }
123 }
124
125 return Collections.unmodifiableList(extensions);
126 }
127
128 private CoreExtensionEntry createExtension(CoreExtension extension, List<Artifact> artifacts) throws Exception {
129 String realmId = "coreExtension>" + extension.getGroupId() + ":" + extension.getArtifactId() + ":"
130 + extension.getVersion();
131 final ClassRealm realm = classWorld.newRealm(realmId, null);
132 Set<String> providedArtifacts = Collections.emptySet();
133 String classLoadingStrategy = extension.getClassLoadingStrategy();
134 if (STRATEGY_PARENT_FIRST.equals(classLoadingStrategy)) {
135 realm.importFrom(parentRealm, "");
136 } else if (STRATEGY_PLUGIN.equals(classLoadingStrategy)) {
137 coreExports.getExportedPackages().forEach((p, cl) -> realm.importFrom(cl, p));
138 providedArtifacts = coreExports.getExportedArtifacts();
139 } else if (STRATEGY_SELF_FIRST.equals(classLoadingStrategy)) {
140 realm.setParentRealm(parentRealm);
141 } else {
142 throw new IllegalArgumentException("Unsupported class-loading strategy '"
143 + classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST
144 + ", " + STRATEGY_PLUGIN + " and " + STRATEGY_SELF_FIRST);
145 }
146 log.debug("Populating class realm {}", realm.getId());
147 for (Artifact artifact : artifacts) {
148 String id = artifact.getGroupId() + ":" + artifact.getArtifactId();
149 if (providedArtifacts.contains(id)) {
150 log.debug(" Excluded {}", id);
151 } else {
152 File file = artifact.getFile();
153 log.debug(" Included {} located at {}", id, file);
154 realm.addURL(file.toURI().toURL());
155 }
156 }
157 return CoreExtensionEntry.discoverFrom(
158 realm,
159 Collections.singleton(artifacts.get(0).getFile()),
160 extension.getGroupId() + ":" + extension.getArtifactId(),
161 extension.getConfiguration());
162 }
163
164 private List<Artifact> resolveExtension(
165 CoreExtension extension,
166 RepositorySystemSession repoSession,
167 List<RemoteRepository> repositories,
168 DependencyFilter dependencyFilter,
169 Interpolator interpolator)
170 throws ExtensionResolutionException {
171 try {
172
173
174
175
176 Plugin plugin = Plugin.newBuilder()
177 .groupId(interpolator.interpolate(extension.getGroupId()))
178 .artifactId(interpolator.interpolate(extension.getArtifactId()))
179 .version(interpolator.interpolate(extension.getVersion()))
180 .build();
181
182 DependencyResult result = pluginDependenciesResolver.resolveCoreExtension(
183 new org.apache.maven.model.Plugin(plugin), dependencyFilter, repositories, repoSession);
184 return result.getArtifactResults().stream()
185 .filter(ArtifactResult::isResolved)
186 .map(ArtifactResult::getArtifact)
187 .collect(Collectors.toList());
188 } catch (PluginResolutionException e) {
189 throw new ExtensionResolutionException(extension, e.getCause());
190 } catch (InterpolationException e) {
191 throw new ExtensionResolutionException(extension, e);
192 }
193 }
194
195 private static Interpolator createInterpolator(MavenExecutionRequest request) {
196 StringSearchInterpolator interpolator = new StringSearchInterpolator();
197 interpolator.addValueSource(new MapBasedValueSource(request.getUserProperties()));
198 interpolator.addValueSource(new MapBasedValueSource(request.getSystemProperties()));
199 return interpolator;
200 }
201 }