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