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