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.model.Build; 037import org.apache.maven.model.Extension; 038import org.apache.maven.model.Model; 039import org.apache.maven.model.Plugin; 040import org.apache.maven.model.Repository; 041import org.apache.maven.plugin.ExtensionRealmCache; 042import org.apache.maven.plugin.PluginArtifactsCache; 043import org.apache.maven.plugin.PluginResolutionException; 044import org.apache.maven.plugin.internal.PluginDependenciesResolver; 045import org.apache.maven.plugin.version.DefaultPluginVersionRequest; 046import org.apache.maven.plugin.version.PluginVersionRequest; 047import org.apache.maven.plugin.version.PluginVersionResolutionException; 048import org.apache.maven.plugin.version.PluginVersionResolver; 049import org.apache.maven.repository.RepositorySystem; 050import org.codehaus.plexus.PlexusContainer; 051import org.codehaus.plexus.classworlds.realm.ClassRealm; 052import org.codehaus.plexus.component.annotations.Component; 053import org.codehaus.plexus.component.annotations.Requirement; 054import org.codehaus.plexus.logging.Logger; 055import org.eclipse.aether.artifact.Artifact; 056import org.eclipse.aether.graph.DependencyFilter; 057import org.eclipse.aether.graph.DependencyNode; 058import org.eclipse.aether.repository.RemoteRepository; 059import org.eclipse.aether.util.filter.ExclusionsDependencyFilter; 060import org.eclipse.aether.util.graph.visitor.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 ) 070public 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}