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