001 package org.apache.maven.project;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.IOException;
023 import java.util.ArrayList;
024 import java.util.Arrays;
025 import java.util.Collection;
026 import java.util.HashMap;
027 import java.util.HashSet;
028 import java.util.LinkedHashSet;
029 import java.util.List;
030 import java.util.Map;
031 import java.util.Set;
032
033 import org.apache.maven.artifact.InvalidRepositoryException;
034 import org.apache.maven.artifact.repository.ArtifactRepository;
035 import org.apache.maven.classrealm.ClassRealmManager;
036 import org.apache.maven.model.Build;
037 import org.apache.maven.model.Extension;
038 import org.apache.maven.model.Model;
039 import org.apache.maven.model.Plugin;
040 import org.apache.maven.model.Repository;
041 import org.apache.maven.plugin.ExtensionRealmCache;
042 import org.apache.maven.plugin.PluginArtifactsCache;
043 import org.apache.maven.plugin.PluginResolutionException;
044 import org.apache.maven.plugin.internal.PluginDependenciesResolver;
045 import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
046 import org.apache.maven.plugin.version.PluginVersionRequest;
047 import org.apache.maven.plugin.version.PluginVersionResolutionException;
048 import org.apache.maven.plugin.version.PluginVersionResolver;
049 import org.apache.maven.repository.RepositorySystem;
050 import org.codehaus.plexus.PlexusContainer;
051 import org.codehaus.plexus.classworlds.realm.ClassRealm;
052 import org.codehaus.plexus.component.annotations.Component;
053 import org.codehaus.plexus.component.annotations.Requirement;
054 import org.codehaus.plexus.logging.Logger;
055 import org.sonatype.aether.artifact.Artifact;
056 import org.sonatype.aether.graph.DependencyFilter;
057 import org.sonatype.aether.graph.DependencyNode;
058 import org.sonatype.aether.repository.RemoteRepository;
059 import org.sonatype.aether.util.filter.ExclusionsDependencyFilter;
060 import org.sonatype.aether.util.graph.PreorderNodeListGenerator;
061
062 /**
063 * Assists the project builder. <strong>Warning:</strong> This is an internal utility class that is only public for
064 * technical reasons, it is not part of the public API. In particular, this class can be changed or deleted without
065 * prior notice.
066 *
067 * @author Benjamin Bentmann
068 */
069 @Component( role = ProjectBuildingHelper.class )
070 public class DefaultProjectBuildingHelper
071 implements ProjectBuildingHelper
072 {
073
074 @Requirement
075 private Logger logger;
076
077 @Requirement
078 private PlexusContainer container;
079
080 @Requirement
081 private ClassRealmManager classRealmManager;
082
083 @Requirement
084 private PluginArtifactsCache pluginArtifactsCache;
085
086 @Requirement
087 private ExtensionRealmCache extensionRealmCache;
088
089 @Requirement
090 private ProjectRealmCache projectRealmCache;
091
092 @Requirement
093 private RepositorySystem repositorySystem;
094
095 @Requirement
096 private PluginVersionResolver pluginVersionResolver;
097
098 @Requirement
099 private PluginDependenciesResolver pluginDependenciesResolver;
100
101 private ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder();
102
103 public List<ArtifactRepository> createArtifactRepositories( List<Repository> pomRepositories,
104 List<ArtifactRepository> externalRepositories,
105 ProjectBuildingRequest request )
106 throws InvalidRepositoryException
107 {
108 List<ArtifactRepository> internalRepositories = new ArrayList<ArtifactRepository>();
109
110 for ( Repository repository : pomRepositories )
111 {
112 internalRepositories.add( repositorySystem.buildArtifactRepository( repository ) );
113 }
114
115 repositorySystem.injectMirror( request.getRepositorySession(), internalRepositories );
116
117 repositorySystem.injectProxy( request.getRepositorySession(), internalRepositories );
118
119 repositorySystem.injectAuthentication( request.getRepositorySession(), internalRepositories );
120
121 List<ArtifactRepository> dominantRepositories;
122 List<ArtifactRepository> recessiveRepositories;
123
124 if ( ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT.equals( request.getRepositoryMerging() ) )
125 {
126 dominantRepositories = externalRepositories;
127 recessiveRepositories = internalRepositories;
128 }
129 else
130 {
131 dominantRepositories = internalRepositories;
132 recessiveRepositories = externalRepositories;
133 }
134
135 List<ArtifactRepository> artifactRepositories = new ArrayList<ArtifactRepository>();
136 Collection<String> repoIds = new HashSet<String>();
137
138 if ( dominantRepositories != null )
139 {
140 for ( ArtifactRepository repository : dominantRepositories )
141 {
142 repoIds.add( repository.getId() );
143 artifactRepositories.add( repository );
144 }
145 }
146
147 if ( recessiveRepositories != null )
148 {
149 for ( ArtifactRepository repository : recessiveRepositories )
150 {
151 if ( repoIds.add( repository.getId() ) )
152 {
153 artifactRepositories.add( repository );
154 }
155 }
156 }
157
158 artifactRepositories = repositorySystem.getEffectiveRepositories( artifactRepositories );
159
160 return artifactRepositories;
161 }
162
163 public synchronized ProjectRealmCache.CacheRecord createProjectRealm( MavenProject project, Model model,
164 ProjectBuildingRequest request )
165 throws PluginResolutionException, PluginVersionResolutionException
166 {
167 ClassRealm projectRealm = null;
168
169 List<Plugin> extensionPlugins = new ArrayList<Plugin>();
170
171 Build build = model.getBuild();
172
173 if ( build != null )
174 {
175 for ( Extension extension : build.getExtensions() )
176 {
177 Plugin plugin = new Plugin();
178 plugin.setGroupId( extension.getGroupId() );
179 plugin.setArtifactId( extension.getArtifactId() );
180 plugin.setVersion( extension.getVersion() );
181 extensionPlugins.add( plugin );
182 }
183
184 for ( Plugin plugin : build.getPlugins() )
185 {
186 if ( plugin.isExtensions() )
187 {
188 extensionPlugins.add( plugin );
189 }
190 }
191 }
192
193 if ( extensionPlugins.isEmpty() )
194 {
195 if ( logger.isDebugEnabled() )
196 {
197 logger.debug( "Extension realms for project " + model.getId() + ": (none)" );
198 }
199
200 return new ProjectRealmCache.CacheRecord( null, null );
201 }
202
203 List<ClassRealm> extensionRealms = new ArrayList<ClassRealm>();
204
205 Map<ClassRealm, List<String>> exportedPackages = new HashMap<ClassRealm, List<String>>();
206
207 Map<ClassRealm, List<String>> exportedArtifacts = new HashMap<ClassRealm, List<String>>();
208
209 List<Artifact> publicArtifacts = new ArrayList<Artifact>();
210
211 for ( Plugin plugin : extensionPlugins )
212 {
213 if ( plugin.getVersion() == null )
214 {
215 PluginVersionRequest versionRequest =
216 new DefaultPluginVersionRequest( plugin, request.getRepositorySession(),
217 project.getRemotePluginRepositories() );
218 plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() );
219 }
220
221 List<Artifact> artifacts;
222
223 PluginArtifactsCache.Key cacheKey =
224 pluginArtifactsCache.createKey( plugin, null, project.getRemotePluginRepositories(),
225 request.getRepositorySession() );
226
227 PluginArtifactsCache.CacheRecord recordArtifacts = pluginArtifactsCache.get( cacheKey );
228
229 if ( recordArtifacts != null )
230 {
231 artifacts = recordArtifacts.artifacts;
232 }
233 else
234 {
235 try
236 {
237 artifacts = resolveExtensionArtifacts( plugin, project.getRemotePluginRepositories(), request );
238
239 recordArtifacts = pluginArtifactsCache.put( cacheKey, artifacts );
240 }
241 catch ( PluginResolutionException e )
242 {
243 pluginArtifactsCache.put( cacheKey, e );
244
245 pluginArtifactsCache.register( project, recordArtifacts );
246
247 throw e;
248 }
249 }
250
251 pluginArtifactsCache.register( project, recordArtifacts );
252
253 ClassRealm extensionRealm;
254 ExtensionDescriptor extensionDescriptor = null;
255
256 ExtensionRealmCache.CacheRecord recordRealm = extensionRealmCache.get( artifacts );
257
258 if ( recordRealm != null )
259 {
260 extensionRealm = recordRealm.realm;
261 extensionDescriptor = recordRealm.desciptor;
262 }
263 else
264 {
265 extensionRealm = classRealmManager.createExtensionRealm( plugin, artifacts );
266
267 try
268 {
269 container.discoverComponents( extensionRealm );
270 }
271 catch ( Exception e )
272 {
273 throw new IllegalStateException( "Failed to discover components in extension realm "
274 + extensionRealm.getId(), e );
275 }
276
277 Artifact extensionArtifact = artifacts.get( 0 );
278 try
279 {
280 extensionDescriptor = extensionDescriptorBuilder.build( extensionArtifact.getFile() );
281 }
282 catch ( IOException e )
283 {
284 String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage();
285 if ( logger.isDebugEnabled() )
286 {
287 logger.error( message, e );
288 }
289 else
290 {
291 logger.error( message );
292 }
293 }
294
295 recordRealm = extensionRealmCache.put( artifacts, extensionRealm, extensionDescriptor );
296 }
297
298 extensionRealmCache.register( project, recordRealm );
299
300 extensionRealms.add( extensionRealm );
301 if ( extensionDescriptor != null )
302 {
303 exportedPackages.put( extensionRealm, extensionDescriptor.getExportedPackages() );
304 exportedArtifacts.put( extensionRealm, extensionDescriptor.getExportedArtifacts() );
305 }
306
307 if ( !plugin.isExtensions() && artifacts.size() == 2 && artifacts.get( 0 ).getFile() != null
308 && "plexus-utils".equals( artifacts.get( 1 ).getArtifactId() ) )
309 {
310 /*
311 * This is purely for backward-compat with 2.x where <extensions> consisting of a single artifact where
312 * loaded into the core and hence available to plugins, in contrast to bigger extensions that were
313 * loaded into a dedicated realm which is invisible to plugins (MNG-2749).
314 */
315 publicArtifacts.add( artifacts.get( 0 ) );
316 }
317 }
318
319 if ( logger.isDebugEnabled() )
320 {
321 logger.debug( "Extension realms for project " + model.getId() + ": " + extensionRealms );
322 }
323
324 ProjectRealmCache.CacheRecord record = projectRealmCache.get( extensionRealms );
325
326 if ( record == null )
327 {
328 projectRealm = classRealmManager.createProjectRealm( model, publicArtifacts );
329
330 Set<String> exclusions = new LinkedHashSet<String>();
331
332 for ( ClassRealm extensionRealm : extensionRealms )
333 {
334 List<String> excludes = exportedArtifacts.get( extensionRealm );
335
336 if ( excludes != null )
337 {
338 exclusions.addAll( excludes );
339 }
340
341 List<String> exports = exportedPackages.get( extensionRealm );
342
343 if ( exports == null || exports.isEmpty() )
344 {
345 /*
346 * Most existing extensions don't define exported packages, i.e. no classes are to be exposed to
347 * plugins, yet the components provided by the extension (e.g. artifact handlers) must be
348 * accessible, i.e. we still must import the extension realm into the project realm.
349 */
350 exports = Arrays.asList( extensionRealm.getId() );
351 }
352
353 for ( String export : exports )
354 {
355 projectRealm.importFrom( extensionRealm, export );
356 }
357 }
358
359 DependencyFilter extensionArtifactFilter = null;
360 if ( !exclusions.isEmpty() )
361 {
362 extensionArtifactFilter = new ExclusionsDependencyFilter( exclusions );
363 }
364
365 record = projectRealmCache.put( extensionRealms, projectRealm, extensionArtifactFilter );
366 }
367
368 projectRealmCache.register( project, record );
369
370 return record;
371 }
372
373 private List<Artifact> resolveExtensionArtifacts( Plugin extensionPlugin, List<RemoteRepository> repositories,
374 ProjectBuildingRequest request )
375 throws PluginResolutionException
376 {
377 DependencyNode root =
378 pluginDependenciesResolver.resolve( extensionPlugin, null, null, repositories,
379 request.getRepositorySession() );
380
381 PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
382 root.accept( nlg );
383 return nlg.getArtifacts( false );
384 }
385
386 public void selectProjectRealm( MavenProject project )
387 {
388 ClassLoader projectRealm = project.getClassRealm();
389
390 if ( projectRealm == null )
391 {
392 projectRealm = classRealmManager.getCoreRealm();
393 }
394
395 Thread.currentThread().setContextClassLoader( projectRealm );
396 }
397
398 }