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