1 package org.apache.maven.cli.internal;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Set;
27
28 import javax.inject.Inject;
29 import javax.inject.Named;
30
31 import org.apache.maven.RepositoryUtils;
32 import org.apache.maven.cli.internal.extension.model.CoreExtension;
33 import org.apache.maven.execution.MavenExecutionRequest;
34 import org.apache.maven.extension.internal.CoreExports;
35 import org.apache.maven.extension.internal.CoreExtensionEntry;
36 import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
37 import org.apache.maven.api.model.Plugin;
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.artifact.Artifact;
50 import org.eclipse.aether.graph.DependencyFilter;
51 import org.eclipse.aether.graph.DependencyNode;
52 import org.eclipse.aether.repository.RemoteRepository;
53 import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
54 import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58
59
60
61 @Named
62 public class BootstrapCoreExtensionManager
63 {
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( DefaultPluginDependenciesResolver pluginDependenciesResolver,
82 DefaultRepositorySystemSessionFactory repositorySystemSessionFactory,
83 CoreExports coreExports,
84 PlexusContainer container )
85 {
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( MavenExecutionRequest request, Set<String> providedArtifacts,
94 List<CoreExtension> extensions )
95 throws Exception
96 {
97 RepositorySystemSession repoSession = repositorySystemSessionFactory.newRepositorySession( request );
98 List<RemoteRepository> repositories = RepositoryUtils.toRepos( request.getPluginArtifactRepositories() );
99 Interpolator interpolator = createInterpolator( request );
100
101 return resolveCoreExtensions( repoSession, repositories, providedArtifacts, extensions, interpolator );
102 }
103
104 private List<CoreExtensionEntry> resolveCoreExtensions( RepositorySystemSession repoSession,
105 List<RemoteRepository> repositories,
106 Set<String> providedArtifacts,
107 List<CoreExtension> configuration,
108 Interpolator interpolator )
109 throws Exception
110 {
111 List<CoreExtensionEntry> extensions = new ArrayList<>();
112
113 DependencyFilter dependencyFilter = new ExclusionsDependencyFilter( providedArtifacts );
114
115 for ( CoreExtension extension : configuration )
116 {
117 List<Artifact> artifacts = resolveExtension( extension, repoSession, repositories,
118 dependencyFilter, interpolator );
119 if ( !artifacts.isEmpty() )
120 {
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 )
129 throws Exception
130 {
131 String realmId =
132 "coreExtension>" + extension.getGroupId() + ":" + extension.getArtifactId() + ":" + extension.getVersion();
133 final ClassRealm realm = classWorld.newRealm( realmId, null );
134 Set<String> providedArtifacts = Collections.emptySet();
135 String classLoadingStrategy = extension.getClassLoadingStrategy();
136 if ( STRATEGY_PARENT_FIRST.equals( classLoadingStrategy ) )
137 {
138 realm.importFrom( parentRealm, "" );
139 }
140 else if ( STRATEGY_PLUGIN.equals( classLoadingStrategy ) )
141 {
142 coreExports.getExportedPackages().forEach( ( p, cl ) -> realm.importFrom( cl, p ) );
143 providedArtifacts = coreExports.getExportedArtifacts();
144 }
145 else if ( STRATEGY_SELF_FIRST.equals( classLoadingStrategy ) )
146 {
147 realm.setParentRealm( parentRealm );
148 }
149 else
150 {
151 throw new IllegalArgumentException( "Unsupported class-loading strategy '"
152 + classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST
153 + ", " + STRATEGY_PLUGIN + " and " + STRATEGY_SELF_FIRST );
154 }
155 log.debug( "Populating class realm {}", realm.getId() );
156 for ( Artifact artifact : artifacts )
157 {
158 String id = artifact.getGroupId() + ":" + artifact.getArtifactId();
159 if ( providedArtifacts.contains( id ) )
160 {
161 log.debug( " Excluded {}", id );
162 }
163 else
164 {
165 File file = artifact.getFile();
166 log.debug( " Included {} located at {}", id, file );
167 realm.addURL( file.toURI().toURL() );
168 }
169 }
170 return CoreExtensionEntry.discoverFrom( realm, Collections.singleton( artifacts.get( 0 ).getFile() ) );
171 }
172
173 private List<Artifact> resolveExtension( CoreExtension extension, RepositorySystemSession repoSession,
174 List<RemoteRepository> repositories, DependencyFilter dependencyFilter,
175 Interpolator interpolator )
176 throws ExtensionResolutionException
177 {
178 try
179 {
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 DependencyNode root = pluginDependenciesResolver
191 .resolveCoreExtension( new org.apache.maven.model.Plugin( plugin ),
192 dependencyFilter, repositories, repoSession );
193 PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
194 root.accept( nlg );
195
196 return nlg.getArtifacts( false );
197 }
198 catch ( PluginResolutionException e )
199 {
200 throw new ExtensionResolutionException( extension, e.getCause() );
201 }
202 catch ( InterpolationException e )
203 {
204 throw new ExtensionResolutionException( extension, e );
205 }
206 }
207
208 private static Interpolator createInterpolator( MavenExecutionRequest request )
209 {
210 StringSearchInterpolator interpolator = new StringSearchInterpolator();
211 interpolator.addValueSource( new MapBasedValueSource( request.getUserProperties() ) );
212 interpolator.addValueSource( new MapBasedValueSource( request.getSystemProperties() ) );
213 return interpolator;
214 }
215
216 }