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