View Javadoc
1   package org.apache.maven.cli.internal;
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.File;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Map.Entry;
27  import java.util.Set;
28  
29  import javax.inject.Inject;
30  import javax.inject.Named;
31  
32  import org.apache.maven.RepositoryUtils;
33  import org.apache.maven.cli.internal.extension.model.CoreExtension;
34  import org.apache.maven.execution.MavenExecutionRequest;
35  import org.apache.maven.extension.internal.CoreExports;
36  import org.apache.maven.extension.internal.CoreExtensionEntry;
37  import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
38  import org.apache.maven.model.Plugin;
39  import org.apache.maven.plugin.PluginResolutionException;
40  import org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver;
41  import org.codehaus.plexus.DefaultPlexusContainer;
42  import org.codehaus.plexus.PlexusContainer;
43  import org.codehaus.plexus.classworlds.ClassWorld;
44  import org.codehaus.plexus.classworlds.realm.ClassRealm;
45  import org.codehaus.plexus.interpolation.InterpolationException;
46  import org.codehaus.plexus.interpolation.Interpolator;
47  import org.codehaus.plexus.interpolation.MapBasedValueSource;
48  import org.codehaus.plexus.interpolation.StringSearchInterpolator;
49  import org.codehaus.plexus.logging.Logger;
50  import org.eclipse.aether.RepositorySystemSession;
51  import org.eclipse.aether.artifact.Artifact;
52  import org.eclipse.aether.graph.DependencyFilter;
53  import org.eclipse.aether.graph.DependencyNode;
54  import org.eclipse.aether.repository.RemoteRepository;
55  import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
56  import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
57  
58  /**
59   * BootstrapCoreExtensionManager
60   */
61  @Named
62  public class BootstrapCoreExtensionManager
63  {
64      public static final String STRATEGY_PARENT_FIRST = "parent-first";
65      public static final String STRATEGY_PLUGIN = "plugin";
66      public static final String STRATEGY_SELF_FIRST = "self-first";
67  
68      private final Logger log;
69  
70      private final DefaultPluginDependenciesResolver pluginDependenciesResolver;
71  
72      private final DefaultRepositorySystemSessionFactory repositorySystemSessionFactory;
73  
74      private final CoreExports coreExports;
75  
76      private final ClassWorld classWorld;
77  
78      private final ClassRealm parentRealm;
79  
80      @Inject
81      public BootstrapCoreExtensionManager( Logger log, DefaultPluginDependenciesResolver pluginDependenciesResolver,
82                                            DefaultRepositorySystemSessionFactory repositorySystemSessionFactory,
83                                            CoreExports coreExports,
84                                            PlexusContainer container )
85      {
86          this.log = log;
87          this.pluginDependenciesResolver = pluginDependenciesResolver;
88          this.repositorySystemSessionFactory = repositorySystemSessionFactory;
89          this.coreExports = coreExports;
90          this.classWorld = ( (DefaultPlexusContainer) container ).getClassWorld();
91          this.parentRealm = container.getContainerRealm();
92      }
93  
94      public List<CoreExtensionEntry> loadCoreExtensions( MavenExecutionRequest request, Set<String> providedArtifacts,
95                                                          List<CoreExtension> extensions )
96          throws Exception
97      {
98          RepositorySystemSession repoSession = repositorySystemSessionFactory.newRepositorySession( request );
99          List<RemoteRepository> repositories = RepositoryUtils.toRepos( request.getPluginArtifactRepositories() );
100         Interpolator interpolator = createInterpolator( request );
101 
102         return resolveCoreExtensions( repoSession, repositories, providedArtifacts, extensions, interpolator );
103     }
104 
105     private List<CoreExtensionEntry> resolveCoreExtensions( RepositorySystemSession repoSession,
106                                                             List<RemoteRepository> repositories,
107                                                             Set<String> providedArtifacts,
108                                                             List<CoreExtension> configuration,
109                                                             Interpolator interpolator )
110         throws Exception
111     {
112         List<CoreExtensionEntry> extensions = new ArrayList<>();
113 
114         DependencyFilter dependencyFilter = new ExclusionsDependencyFilter( providedArtifacts );
115 
116         for ( CoreExtension extension : configuration )
117         {
118             List<Artifact> artifacts = resolveExtension( extension, repoSession, repositories,
119                                                          dependencyFilter, interpolator );
120             if ( !artifacts.isEmpty() )
121             {
122                 extensions.add( createExtension( extension, artifacts ) );
123             }
124         }
125 
126         return Collections.unmodifiableList( extensions );
127     }
128 
129     private CoreExtensionEntry createExtension( CoreExtension extension, List<Artifact> artifacts )
130         throws Exception
131     {
132         String realmId =
133             "coreExtension>" + extension.getGroupId() + ":" + extension.getArtifactId() + ":" + extension.getVersion();
134         final ClassRealm realm = classWorld.newRealm( realmId, null );
135         Set<String> providedArtifacts = Collections.emptySet();
136         String classLoadingStrategy = extension.getClassLoadingStrategy();
137         if ( STRATEGY_PARENT_FIRST.equals( classLoadingStrategy ) )
138         {
139             realm.importFrom( parentRealm, "" );
140         }
141         else if ( STRATEGY_PLUGIN.equals( classLoadingStrategy ) )
142         {
143             for ( Entry<String, ClassLoader> entry : coreExports.getExportedPackages().entrySet() )
144             {
145                 realm.importFrom( entry.getValue(), entry.getKey() );
146             }
147             providedArtifacts = coreExports.getExportedArtifacts();
148         }
149         else if ( STRATEGY_SELF_FIRST.equals( classLoadingStrategy ) )
150         {
151             realm.setParentRealm( parentRealm );
152         }
153         else
154         {
155             throw new IllegalArgumentException( "Unsupported class-loading strategy '"
156                     + classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST
157                     + ", " + STRATEGY_PLUGIN + " and " + STRATEGY_SELF_FIRST );
158         }
159         log.debug( "Populating class realm " + realm.getId() );
160         for ( Artifact artifact : artifacts )
161         {
162             String id = artifact.getGroupId() + ":" + artifact.getArtifactId();
163             if ( providedArtifacts.contains( id ) )
164             {
165                 log.debug( "  Excluded " + id );
166             }
167             else
168             {
169                 File file = artifact.getFile();
170                 log.debug( "  Included " + id + " located at " + file );
171                 realm.addURL( file.toURI().toURL() );
172             }
173         }
174         return CoreExtensionEntry.discoverFrom( realm, Collections.singleton( artifacts.get( 0 ).getFile() ) );
175     }
176 
177     private List<Artifact> resolveExtension( CoreExtension extension, RepositorySystemSession repoSession,
178                                              List<RemoteRepository> repositories, DependencyFilter dependencyFilter,
179                                              Interpolator interpolator )
180         throws ExtensionResolutionException
181     {
182         try
183         {
184             /* TODO: Enhance the PluginDependenciesResolver to provide a
185              * resolveCoreExtension method which uses a CoreExtension
186              * object instead of a Plugin as this makes no sense.
187              */
188             Plugin plugin = new Plugin();
189             plugin.setGroupId( interpolator.interpolate( extension.getGroupId() ) );
190             plugin.setArtifactId( interpolator.interpolate( extension.getArtifactId() ) );
191             plugin.setVersion( interpolator.interpolate( extension.getVersion() ) );
192 
193             DependencyNode root = pluginDependenciesResolver
194                     .resolveCoreExtension( plugin, dependencyFilter, repositories, repoSession );
195             PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
196             root.accept( nlg );
197             List<Artifact> artifacts = nlg.getArtifacts( false );
198 
199             return artifacts;
200         }
201         catch ( PluginResolutionException e )
202         {
203             throw new ExtensionResolutionException( extension, e.getCause() );
204         }
205         catch ( InterpolationException e )
206         {
207             throw new ExtensionResolutionException( extension, e );
208         }
209     }
210 
211     private static Interpolator createInterpolator( MavenExecutionRequest request )
212     {
213         StringSearchInterpolator interpolator = new StringSearchInterpolator();
214         interpolator.addValueSource( new MapBasedValueSource( request.getUserProperties() ) );
215         interpolator.addValueSource( new MapBasedValueSource( request.getSystemProperties() ) );
216         return interpolator;
217     }
218 
219 }