View Javadoc

1   package org.apache.maven.project;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.LinkedHashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.maven.artifact.InvalidRepositoryException;
34  import org.apache.maven.artifact.repository.ArtifactRepository;
35  import org.apache.maven.classrealm.ClassRealmManager;
36  import org.apache.maven.model.Build;
37  import org.apache.maven.model.Extension;
38  import org.apache.maven.model.Model;
39  import org.apache.maven.model.Plugin;
40  import org.apache.maven.model.Repository;
41  import org.apache.maven.plugin.ExtensionRealmCache;
42  import org.apache.maven.plugin.PluginArtifactsCache;
43  import org.apache.maven.plugin.PluginResolutionException;
44  import org.apache.maven.plugin.internal.PluginDependenciesResolver;
45  import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
46  import org.apache.maven.plugin.version.PluginVersionRequest;
47  import org.apache.maven.plugin.version.PluginVersionResolutionException;
48  import org.apache.maven.plugin.version.PluginVersionResolver;
49  import org.apache.maven.repository.RepositorySystem;
50  import org.codehaus.plexus.PlexusContainer;
51  import org.codehaus.plexus.classworlds.realm.ClassRealm;
52  import org.codehaus.plexus.component.annotations.Component;
53  import org.codehaus.plexus.component.annotations.Requirement;
54  import org.codehaus.plexus.logging.Logger;
55  import org.sonatype.aether.artifact.Artifact;
56  import org.sonatype.aether.graph.DependencyFilter;
57  import org.sonatype.aether.graph.DependencyNode;
58  import org.sonatype.aether.repository.RemoteRepository;
59  import org.sonatype.aether.util.filter.ExclusionsDependencyFilter;
60  import org.sonatype.aether.util.graph.PreorderNodeListGenerator;
61  
62  /**
63   * Assists the project builder. <strong>Warning:</strong> This is an internal utility class that is only public for
64   * technical reasons, it is not part of the public API. In particular, this class can be changed or deleted without
65   * prior notice.
66   * 
67   * @author Benjamin Bentmann
68   */
69  @Component( role = ProjectBuildingHelper.class )
70  public class DefaultProjectBuildingHelper
71      implements ProjectBuildingHelper
72  {
73  
74      @Requirement
75      private Logger logger;
76  
77      @Requirement
78      private PlexusContainer container;
79  
80      @Requirement
81      private ClassRealmManager classRealmManager;
82  
83      @Requirement
84      private PluginArtifactsCache pluginArtifactsCache;
85  
86      @Requirement
87      private ExtensionRealmCache extensionRealmCache;
88  
89      @Requirement
90      private ProjectRealmCache projectRealmCache;
91  
92      @Requirement
93      private RepositorySystem repositorySystem;
94  
95      @Requirement
96      private PluginVersionResolver pluginVersionResolver;
97  
98      @Requirement
99      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 }