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.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.maven.RepositoryUtils;
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.artifact.InvalidRepositoryException;
35  import org.apache.maven.artifact.repository.ArtifactRepository;
36  import org.apache.maven.classrealm.ClassRealmManager;
37  import org.apache.maven.model.Build;
38  import org.apache.maven.model.Extension;
39  import org.apache.maven.model.Model;
40  import org.apache.maven.model.Plugin;
41  import org.apache.maven.model.Repository;
42  import org.apache.maven.plugin.ExtensionRealmCache;
43  import org.apache.maven.plugin.MavenPluginManager;
44  import org.apache.maven.plugin.PluginManagerException;
45  import org.apache.maven.plugin.PluginResolutionException;
46  import org.apache.maven.plugin.version.PluginVersionResolutionException;
47  import org.apache.maven.repository.RepositorySystem;
48  import org.codehaus.plexus.PlexusContainer;
49  import org.codehaus.plexus.classworlds.realm.ClassRealm;
50  import org.codehaus.plexus.component.annotations.Component;
51  import org.codehaus.plexus.component.annotations.Requirement;
52  import org.codehaus.plexus.logging.Logger;
53  import org.eclipse.aether.graph.DependencyFilter;
54  import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
55  
56  /**
57   * Assists the project builder. <strong>Warning:</strong> This is an internal utility class that is only public for
58   * technical reasons, it is not part of the public API. In particular, this class can be changed or deleted without
59   * prior notice.
60   *
61   * @author Benjamin Bentmann
62   */
63  @Component( role = ProjectBuildingHelper.class )
64  public class DefaultProjectBuildingHelper
65      implements ProjectBuildingHelper
66  {
67  
68      @Requirement
69      private Logger logger;
70  
71      @Requirement
72      private PlexusContainer container;
73  
74      @Requirement
75      private ClassRealmManager classRealmManager;
76  
77      @Requirement
78      private ProjectRealmCache projectRealmCache;
79  
80      @Requirement
81      private RepositorySystem repositorySystem;
82  
83      @Requirement
84      private MavenPluginManager pluginManager;
85  
86      public List<ArtifactRepository> createArtifactRepositories( List<Repository> pomRepositories,
87                                                                  List<ArtifactRepository> externalRepositories,
88                                                                  ProjectBuildingRequest request )
89          throws InvalidRepositoryException
90      {
91          List<ArtifactRepository> internalRepositories = new ArrayList<>();
92  
93          for ( Repository repository : pomRepositories )
94          {
95              internalRepositories.add( repositorySystem.buildArtifactRepository( repository ) );
96          }
97  
98          repositorySystem.injectMirror( request.getRepositorySession(), internalRepositories );
99  
100         repositorySystem.injectProxy( request.getRepositorySession(), internalRepositories );
101 
102         repositorySystem.injectAuthentication( request.getRepositorySession(), internalRepositories );
103 
104         List<ArtifactRepository> dominantRepositories;
105         List<ArtifactRepository> recessiveRepositories;
106 
107         if ( ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT.equals( request.getRepositoryMerging() ) )
108         {
109             dominantRepositories = externalRepositories;
110             recessiveRepositories = internalRepositories;
111         }
112         else
113         {
114             dominantRepositories = internalRepositories;
115             recessiveRepositories = externalRepositories;
116         }
117 
118         List<ArtifactRepository> artifactRepositories = new ArrayList<>();
119         Collection<String> repoIds = new HashSet<>();
120 
121         if ( dominantRepositories != null )
122         {
123             for ( ArtifactRepository repository : dominantRepositories )
124             {
125                 repoIds.add( repository.getId() );
126                 artifactRepositories.add( repository );
127             }
128         }
129 
130         if ( recessiveRepositories != null )
131         {
132             for ( ArtifactRepository repository : recessiveRepositories )
133             {
134                 if ( repoIds.add( repository.getId() ) )
135                 {
136                     artifactRepositories.add( repository );
137                 }
138             }
139         }
140 
141         artifactRepositories = repositorySystem.getEffectiveRepositories( artifactRepositories );
142 
143         return artifactRepositories;
144     }
145 
146     public synchronized ProjectRealmCache.CacheRecord createProjectRealm( MavenProject project, Model model,
147                                                                           ProjectBuildingRequest request )
148         throws PluginResolutionException, PluginVersionResolutionException, PluginManagerException
149     {
150         ClassRealm projectRealm;
151 
152         List<Plugin> extensionPlugins = new ArrayList<>();
153 
154         Build build = model.getBuild();
155 
156         if ( build != null )
157         {
158             for ( Extension extension : build.getExtensions() )
159             {
160                 Plugin plugin = new Plugin();
161                 plugin.setGroupId( extension.getGroupId() );
162                 plugin.setArtifactId( extension.getArtifactId() );
163                 plugin.setVersion( extension.getVersion() );
164                 extensionPlugins.add( plugin );
165             }
166 
167             for ( Plugin plugin : build.getPlugins() )
168             {
169                 if ( plugin.isExtensions() )
170                 {
171                     extensionPlugins.add( plugin );
172                 }
173             }
174         }
175 
176         if ( extensionPlugins.isEmpty() )
177         {
178             if ( logger.isDebugEnabled() )
179             {
180                 logger.debug( "Extension realms for project " + model.getId() + ": (none)" );
181             }
182 
183             return new ProjectRealmCache.CacheRecord( null, null );
184         }
185 
186         List<ClassRealm> extensionRealms = new ArrayList<>();
187 
188         Map<ClassRealm, List<String>> exportedPackages = new HashMap<>();
189 
190         Map<ClassRealm, List<String>> exportedArtifacts = new HashMap<>();
191 
192         List<Artifact> publicArtifacts = new ArrayList<>();
193 
194         for ( Plugin plugin : extensionPlugins )
195         {
196             ExtensionRealmCache.CacheRecord recordRealm =
197                 pluginManager.setupExtensionsRealm( project, plugin, request.getRepositorySession() );
198 
199             final ClassRealm extensionRealm = recordRealm.realm;
200             final ExtensionDescriptor extensionDescriptor = recordRealm.descriptor;
201             final List<Artifact> artifacts = recordRealm.artifacts;
202 
203             extensionRealms.add( extensionRealm );
204             if ( extensionDescriptor != null )
205             {
206                 exportedPackages.put( extensionRealm, extensionDescriptor.getExportedPackages() );
207                 exportedArtifacts.put( extensionRealm, extensionDescriptor.getExportedArtifacts() );
208             }
209 
210             if ( !plugin.isExtensions() && artifacts.size() == 2 && artifacts.get( 0 ).getFile() != null
211                 && "plexus-utils".equals( artifacts.get( 1 ).getArtifactId() ) )
212             {
213                 /*
214                  * This is purely for backward-compat with 2.x where <extensions> consisting of a single artifact where
215                  * loaded into the core and hence available to plugins, in contrast to bigger extensions that were
216                  * loaded into a dedicated realm which is invisible to plugins (MNG-2749).
217                  */
218                 publicArtifacts.add( artifacts.get( 0 ) );
219             }
220         }
221 
222         if ( logger.isDebugEnabled() )
223         {
224             logger.debug( "Extension realms for project " + model.getId() + ": " + extensionRealms );
225         }
226 
227         ProjectRealmCache.Key projectRealmKey = projectRealmCache.createKey( extensionRealms );
228 
229         ProjectRealmCache.CacheRecord record = projectRealmCache.get( projectRealmKey );
230 
231         if ( record == null )
232         {
233             projectRealm = classRealmManager.createProjectRealm( model, toAetherArtifacts( publicArtifacts ) );
234 
235             Set<String> exclusions = new LinkedHashSet<>();
236 
237             for ( ClassRealm extensionRealm : extensionRealms )
238             {
239                 List<String> excludes = exportedArtifacts.get( extensionRealm );
240 
241                 if ( excludes != null )
242                 {
243                     exclusions.addAll( excludes );
244                 }
245 
246                 List<String> exports = exportedPackages.get( extensionRealm );
247 
248                 if ( exports == null || exports.isEmpty() )
249                 {
250                     /*
251                      * Most existing extensions don't define exported packages, i.e. no classes are to be exposed to
252                      * plugins, yet the components provided by the extension (e.g. artifact handlers) must be
253                      * accessible, i.e. we still must import the extension realm into the project realm.
254                      */
255                     exports = Arrays.asList( extensionRealm.getId() );
256                 }
257 
258                 for ( String export : exports )
259                 {
260                     projectRealm.importFrom( extensionRealm, export );
261                 }
262             }
263 
264             DependencyFilter extensionArtifactFilter = null;
265             if ( !exclusions.isEmpty() )
266             {
267                 extensionArtifactFilter = new ExclusionsDependencyFilter( exclusions );
268             }
269 
270             record = projectRealmCache.put( projectRealmKey, projectRealm, extensionArtifactFilter );
271         }
272 
273         projectRealmCache.register( project, projectRealmKey, record );
274 
275         return record;
276     }
277 
278     public void selectProjectRealm( MavenProject project )
279     {
280         ClassLoader projectRealm = project.getClassRealm();
281 
282         if ( projectRealm == null )
283         {
284             projectRealm = classRealmManager.getCoreRealm();
285         }
286 
287         Thread.currentThread().setContextClassLoader( projectRealm );
288     }
289 
290     private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts( final List<Artifact> pluginArtifacts )
291     {
292         return new ArrayList<>( RepositoryUtils.toArtifacts( pluginArtifacts ) );
293     }
294 
295 }