View Javadoc
1   package org.apache.maven.internal.aether;
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.io.IOException;
24  import java.io.InputStream;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.stream.Collectors;
32  
33  import javax.inject.Inject;
34  import javax.inject.Named;
35  
36  import org.apache.maven.RepositoryUtils;
37  import org.apache.maven.api.xml.Dom;
38  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
39  import org.apache.maven.bridge.MavenRepositorySystem;
40  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
41  import org.apache.maven.execution.MavenExecutionRequest;
42  import org.apache.maven.feature.Features;
43  import org.apache.maven.internal.xml.XmlPlexusConfiguration;
44  import org.apache.maven.internal.xml.Xpp3Dom;
45  import org.apache.maven.model.building.TransformerContext;
46  import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
47  import org.apache.maven.rtinfo.RuntimeInformation;
48  import org.apache.maven.settings.Mirror;
49  import org.apache.maven.settings.Proxy;
50  import org.apache.maven.settings.Server;
51  import org.apache.maven.settings.building.SettingsProblem;
52  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
53  import org.apache.maven.settings.crypto.SettingsDecrypter;
54  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
55  import org.codehaus.plexus.configuration.PlexusConfiguration;
56  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
57  import org.eclipse.aether.ConfigurationProperties;
58  import org.eclipse.aether.DefaultRepositorySystemSession;
59  import org.eclipse.aether.RepositorySystem;
60  import org.eclipse.aether.SessionData;
61  import org.eclipse.aether.artifact.Artifact;
62  import org.eclipse.aether.repository.LocalRepository;
63  import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
64  import org.eclipse.aether.repository.RepositoryPolicy;
65  import org.eclipse.aether.repository.WorkspaceReader;
66  import org.eclipse.aether.resolution.ResolutionErrorPolicy;
67  import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
68  import org.eclipse.aether.transform.FileTransformer;
69  import org.eclipse.aether.transform.TransformException;
70  import org.eclipse.aether.util.repository.AuthenticationBuilder;
71  import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
72  import org.eclipse.aether.util.repository.DefaultMirrorSelector;
73  import org.eclipse.aether.util.repository.DefaultProxySelector;
74  import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
75  import org.eclipse.sisu.Nullable;
76  import org.slf4j.Logger;
77  import org.slf4j.LoggerFactory;
78  
79  /**
80   * @since 3.3.0
81   */
82  @Named
83  public class DefaultRepositorySystemSessionFactory
84  {
85      private static final String MAVEN_RESOLVER_TRANSPORT_KEY = "maven.resolver.transport";
86  
87      private static final String MAVEN_RESOLVER_TRANSPORT_DEFAULT = "default";
88  
89      private static final String MAVEN_RESOLVER_TRANSPORT_WAGON = "wagon";
90  
91      private static final String MAVEN_RESOLVER_TRANSPORT_NATIVE = "native";
92  
93      private static final String MAVEN_RESOLVER_TRANSPORT_AUTO = "auto";
94  
95      private static final String WAGON_TRANSPORTER_PRIORITY_KEY = "aether.priority.WagonTransporterFactory";
96  
97      private static final String NATIVE_HTTP_TRANSPORTER_PRIORITY_KEY = "aether.priority.HttpTransporterFactory";
98  
99      private static final String NATIVE_FILE_TRANSPORTER_PRIORITY_KEY = "aether.priority.FileTransporterFactory";
100 
101     private static final String RESOLVER_MAX_PRIORITY = String.valueOf( Float.MAX_VALUE );
102 
103     private final Logger logger = LoggerFactory.getLogger( getClass() );
104 
105     private final ArtifactHandlerManager artifactHandlerManager;
106 
107     private final RepositorySystem repoSystem;
108 
109     private final LocalRepositoryManagerFactory simpleLocalRepoMgrFactory;
110 
111     private final WorkspaceReader workspaceRepository;
112 
113     private final SettingsDecrypter settingsDecrypter;
114 
115     private final EventSpyDispatcher eventSpyDispatcher;
116 
117     private final MavenRepositorySystem mavenRepositorySystem;
118 
119     private final RuntimeInformation runtimeInformation;
120 
121     @SuppressWarnings( "checkstyle:ParameterNumber" )
122     @Inject
123     public DefaultRepositorySystemSessionFactory(
124             ArtifactHandlerManager artifactHandlerManager,
125             RepositorySystem repoSystem,
126             @Nullable @Named( "simple" ) LocalRepositoryManagerFactory simpleLocalRepoMgrFactory,
127             @Nullable @Named( "ide" ) WorkspaceReader workspaceRepository,
128             SettingsDecrypter settingsDecrypter,
129             EventSpyDispatcher eventSpyDispatcher,
130             MavenRepositorySystem mavenRepositorySystem,
131             RuntimeInformation runtimeInformation )
132     {
133         this.artifactHandlerManager = artifactHandlerManager;
134         this.repoSystem = repoSystem;
135         this.simpleLocalRepoMgrFactory = simpleLocalRepoMgrFactory;
136         this.workspaceRepository = workspaceRepository;
137         this.settingsDecrypter = settingsDecrypter;
138         this.eventSpyDispatcher = eventSpyDispatcher;
139         this.mavenRepositorySystem = mavenRepositorySystem;
140         this.runtimeInformation = runtimeInformation;
141     }
142 
143     @SuppressWarnings( "checkstyle:methodLength" )
144     public DefaultRepositorySystemSession newRepositorySession( MavenExecutionRequest request )
145     {
146         DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
147         session.setCache( request.getRepositoryCache() );
148 
149         Map<Object, Object> configProps = new LinkedHashMap<>();
150         configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() );
151         configProps.put( ConfigurationProperties.INTERACTIVE, request.isInteractiveMode() );
152         configProps.put( "maven.startTime", request.getStartTime() );
153         configProps.putAll( request.getSystemProperties() );
154         configProps.putAll( request.getUserProperties() );
155 
156         session.setOffline( request.isOffline() );
157         session.setChecksumPolicy( request.getGlobalChecksumPolicy() );
158         session.setUpdatePolicy( request.isNoSnapshotUpdates()
159                     ? RepositoryPolicy.UPDATE_POLICY_NEVER
160                     : request.isUpdateSnapshots() ? RepositoryPolicy.UPDATE_POLICY_ALWAYS : null );
161 
162         int errorPolicy = 0;
163         errorPolicy |= request.isCacheNotFound() ? ResolutionErrorPolicy.CACHE_NOT_FOUND
164             : ResolutionErrorPolicy.CACHE_DISABLED;
165         errorPolicy |= request.isCacheTransferError() ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR
166             : ResolutionErrorPolicy.CACHE_DISABLED;
167         session.setResolutionErrorPolicy(
168             new SimpleResolutionErrorPolicy( errorPolicy, errorPolicy | ResolutionErrorPolicy.CACHE_NOT_FOUND ) );
169 
170         session.setArtifactTypeRegistry( RepositoryUtils.newArtifactTypeRegistry( artifactHandlerManager ) );
171 
172         LocalRepository localRepo = new LocalRepository( request.getLocalRepository().getBasedir() );
173 
174         if ( request.isUseLegacyLocalRepository() )
175         {
176             try
177             {
178                 session.setLocalRepositoryManager( simpleLocalRepoMgrFactory.newInstance( session, localRepo ) );
179                 logger.info( "Disabling enhanced local repository: using legacy is strongly discouraged to ensure"
180                                  + " build reproducibility." );
181             }
182             catch ( NoLocalRepositoryManagerException e )
183             {
184                 logger.error( "Failed to configure legacy local repository: falling back to default" );
185                 session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
186             }
187         }
188         else
189         {
190             session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
191         }
192 
193         session.setWorkspaceReader(
194                 request.getWorkspaceReader() != null ? request.getWorkspaceReader() : workspaceRepository );
195 
196         DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
197         decrypt.setProxies( request.getProxies() );
198         decrypt.setServers( request.getServers() );
199         SettingsDecryptionResult decrypted = settingsDecrypter.decrypt( decrypt );
200 
201         if ( logger.isDebugEnabled() )
202         {
203             for ( SettingsProblem problem : decrypted.getProblems() )
204             {
205                 logger.debug( problem.getMessage(), problem.getException() );
206             }
207         }
208 
209         DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
210         for ( Mirror mirror : request.getMirrors() )
211         {
212             mirrorSelector.add( mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.isBlocked(),
213                                 mirror.getMirrorOf(), mirror.getMirrorOfLayouts() );
214         }
215         session.setMirrorSelector( mirrorSelector );
216 
217         DefaultProxySelector proxySelector = new DefaultProxySelector();
218         for ( Proxy proxy : decrypted.getProxies() )
219         {
220             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
221             authBuilder.addUsername( proxy.getUsername() ).addPassword( proxy.getPassword() );
222             proxySelector.add(
223                 new org.eclipse.aether.repository.Proxy( proxy.getProtocol(), proxy.getHost(), proxy.getPort(),
224                                                          authBuilder.build() ), proxy.getNonProxyHosts() );
225         }
226         session.setProxySelector( proxySelector );
227 
228         DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
229         for ( Server server : decrypted.getServers() )
230         {
231             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
232             authBuilder.addUsername( server.getUsername() ).addPassword( server.getPassword() );
233             authBuilder.addPrivateKey( server.getPrivateKey(), server.getPassphrase() );
234             authSelector.add( server.getId(), authBuilder.build() );
235 
236             if ( server.getConfiguration() != null )
237             {
238                 Dom dom = ( ( org.codehaus.plexus.util.xml.Xpp3Dom ) server.getConfiguration() ).getDom();
239                 List<Dom> children = dom.getChildren().stream()
240                         .filter( c -> !"wagonProvider".equals( c.getName() ) )
241                         .collect( Collectors.toList() );
242                 dom = new Xpp3Dom( dom.getName(), null, null, children, null );
243                 PlexusConfiguration config = XmlPlexusConfiguration.toPlexusConfiguration( dom );
244                 configProps.put( "aether.connector.wagon.config." + server.getId(), config );
245             }
246 
247             configProps.put( "aether.connector.perms.fileMode." + server.getId(), server.getFilePermissions() );
248             configProps.put( "aether.connector.perms.dirMode." + server.getId(), server.getDirectoryPermissions() );
249         }
250         session.setAuthenticationSelector( authSelector );
251 
252         Object transport = configProps.getOrDefault( MAVEN_RESOLVER_TRANSPORT_KEY, MAVEN_RESOLVER_TRANSPORT_DEFAULT );
253         if ( MAVEN_RESOLVER_TRANSPORT_DEFAULT.equals( transport ) )
254         {
255             // The "default" mode (user did not set anything) needs to tweak resolver default priorities
256             // that are coded like this (default values):
257             //
258             // org.eclipse.aether.transport.http.HttpTransporterFactory.priority = 5.0f;
259             // org.eclipse.aether.transport.wagon.WagonTransporterFactory.priority = -1.0f;
260             //
261             // Hence, as both are present on classpath, HttpTransport would be selected, while
262             // we want to retain "default" behaviour of Maven and use Wagon. To achieve that,
263             // we set explicitly priority of WagonTransport to 6.0f (just above of HttpTransport),
264             // to make it "win" over HttpTransport. We do this to NOT interfere with possibly
265             // installed OTHER transports and their priorities, as unlike "wagon" or "native"
266             // transport setting, that sets priorities to MAX, hence prevents any 3rd party
267             // transport to get into play (inhibits them), in default mode we want to retain
268             // old behavior. Also, this "default" mode is different from "auto" setting,
269             // as it does not alter resolver priorities at all, and uses priorities as is.
270 
271             configProps.put( WAGON_TRANSPORTER_PRIORITY_KEY, "6" );
272         }
273         else if ( MAVEN_RESOLVER_TRANSPORT_NATIVE.equals( transport ) )
274         {
275             // Make sure (whatever extra priority is set) that resolver native is selected
276             configProps.put( NATIVE_FILE_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY );
277             configProps.put( NATIVE_HTTP_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY );
278         }
279         else if ( MAVEN_RESOLVER_TRANSPORT_WAGON.equals( transport ) )
280         {
281             // Make sure (whatever extra priority is set) that wagon is selected
282             configProps.put( WAGON_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY );
283         }
284         else if ( !MAVEN_RESOLVER_TRANSPORT_AUTO.equals( transport ) )
285         {
286             throw new IllegalArgumentException( "Unknown resolver transport '" + transport
287                     + "'. Supported transports are: " + MAVEN_RESOLVER_TRANSPORT_WAGON + ", "
288                     + MAVEN_RESOLVER_TRANSPORT_NATIVE + ", " + MAVEN_RESOLVER_TRANSPORT_AUTO );
289         }
290 
291         session.setTransferListener( request.getTransferListener() );
292 
293         session.setRepositoryListener( eventSpyDispatcher.chainListener( new LoggingRepositoryListener( logger ) ) );
294 
295         session.setUserProperties( request.getUserProperties() );
296         session.setSystemProperties( request.getSystemProperties() );
297         session.setConfigProperties( configProps );
298 
299         mavenRepositorySystem.injectMirror( request.getRemoteRepositories(), request.getMirrors() );
300         mavenRepositorySystem.injectProxy( session, request.getRemoteRepositories() );
301         mavenRepositorySystem.injectAuthentication( session, request.getRemoteRepositories() );
302 
303         mavenRepositorySystem.injectMirror( request.getPluginArtifactRepositories(), request.getMirrors() );
304         mavenRepositorySystem.injectProxy( session, request.getPluginArtifactRepositories() );
305         mavenRepositorySystem.injectAuthentication( session, request.getPluginArtifactRepositories() );
306 
307         if ( Features.buildConsumer( request.getUserProperties() ).isActive() )
308         {
309             session.setFileTransformerManager( a -> getTransformersForArtifact( a, session.getData() ) );
310         }
311 
312         return session;
313     }
314 
315     private String getUserAgent()
316     {
317         String version = runtimeInformation.getMavenVersion();
318         version = version.isEmpty() ? version : "/" + version;
319         return "Apache-Maven" + version + " (Java " + System.getProperty( "java.version" ) + "; "
320             + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")";
321     }
322 
323     private Collection<FileTransformer> getTransformersForArtifact( final Artifact artifact,
324                                                                     final SessionData sessionData )
325     {
326         TransformerContext context = (TransformerContext) sessionData.get( TransformerContext.KEY );
327         Collection<FileTransformer> transformers = new ArrayList<>();
328 
329         // In case of install:install-file there's no transformer context, as the goal is unrelated to the lifecycle.
330         if ( "pom".equals( artifact.getExtension() ) && context != null )
331         {
332             transformers.add( new FileTransformer()
333             {
334                 @Override
335                 public InputStream transformData( File pomFile )
336                     throws IOException, TransformException
337                 {
338                     try
339                     {
340                         return new ConsumerModelSourceTransformer().transform( pomFile.toPath(), context );
341                     }
342                     catch ( XmlPullParserException e )
343                     {
344                         throw new TransformException( e );
345                     }
346                 }
347 
348                 @Override
349                 public Artifact transformArtifact( Artifact artifact )
350                 {
351                     return artifact;
352                 }
353             } );
354         }
355         return Collections.unmodifiableCollection( transformers );
356     }
357 
358 }