View Javadoc
1   package org.apache.maven.repository.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 org.apache.maven.artifact.ArtifactUtils;
23  import org.apache.maven.artifact.repository.metadata.Versioning;
24  import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
25  import org.eclipse.aether.RepositoryEvent;
26  import org.eclipse.aether.RepositoryEvent.EventType;
27  import org.eclipse.aether.RepositorySystemSession;
28  import org.eclipse.aether.RequestTrace;
29  import org.eclipse.aether.SyncContext;
30  import org.eclipse.aether.impl.MetadataResolver;
31  import org.eclipse.aether.impl.RepositoryEventDispatcher;
32  import org.eclipse.aether.impl.SyncContextFactory;
33  import org.eclipse.aether.impl.VersionRangeResolver;
34  import org.eclipse.aether.metadata.DefaultMetadata;
35  import org.eclipse.aether.metadata.Metadata;
36  import org.eclipse.aether.repository.ArtifactRepository;
37  import org.eclipse.aether.repository.RemoteRepository;
38  import org.eclipse.aether.repository.WorkspaceReader;
39  import org.eclipse.aether.resolution.MetadataRequest;
40  import org.eclipse.aether.resolution.MetadataResult;
41  import org.eclipse.aether.resolution.VersionRangeRequest;
42  import org.eclipse.aether.resolution.VersionRangeResolutionException;
43  import org.eclipse.aether.resolution.VersionRangeResult;
44  import org.eclipse.aether.spi.locator.Service;
45  import org.eclipse.aether.spi.locator.ServiceLocator;
46  import org.eclipse.aether.util.version.GenericVersionScheme;
47  import org.eclipse.aether.version.InvalidVersionSpecificationException;
48  import org.eclipse.aether.version.Version;
49  import org.eclipse.aether.version.VersionConstraint;
50  import org.eclipse.aether.version.VersionScheme;
51  
52  import javax.inject.Inject;
53  import javax.inject.Named;
54  import javax.inject.Singleton;
55  
56  import java.io.FileInputStream;
57  import java.io.InputStream;
58  import java.util.ArrayList;
59  import java.util.Collections;
60  import java.util.HashMap;
61  import java.util.List;
62  import java.util.Map;
63  import java.util.Objects;
64  
65  /**
66   * @author Benjamin Bentmann
67   */
68  @Named
69  @Singleton
70  public class DefaultVersionRangeResolver
71      implements VersionRangeResolver, Service
72  {
73  
74      private static final String MAVEN_METADATA_XML = "maven-metadata.xml";
75  
76      private MetadataResolver metadataResolver;
77  
78      private SyncContextFactory syncContextFactory;
79  
80      private RepositoryEventDispatcher repositoryEventDispatcher;
81  
82      public DefaultVersionRangeResolver()
83      {
84          // enable default constructor
85      }
86  
87      @Inject
88      DefaultVersionRangeResolver( MetadataResolver metadataResolver, SyncContextFactory syncContextFactory,
89                                   RepositoryEventDispatcher repositoryEventDispatcher )
90      {
91          setMetadataResolver( metadataResolver );
92          setSyncContextFactory( syncContextFactory );
93          setRepositoryEventDispatcher( repositoryEventDispatcher );
94      }
95  
96      public void initService( ServiceLocator locator )
97      {
98          setMetadataResolver( locator.getService( MetadataResolver.class ) );
99          setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
100         setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
101     }
102 
103     public DefaultVersionRangeResolver setMetadataResolver( MetadataResolver metadataResolver )
104     {
105         this.metadataResolver = Objects.requireNonNull( metadataResolver, "metadataResolver cannot be null" );
106         return this;
107     }
108 
109     public DefaultVersionRangeResolver setSyncContextFactory( SyncContextFactory syncContextFactory )
110     {
111         this.syncContextFactory = Objects.requireNonNull( syncContextFactory, "syncContextFactory cannot be null" );
112         return this;
113     }
114 
115     public DefaultVersionRangeResolver setRepositoryEventDispatcher(
116         RepositoryEventDispatcher repositoryEventDispatcher )
117     {
118         this.repositoryEventDispatcher = Objects.requireNonNull( repositoryEventDispatcher,
119             "repositoryEventDispatcher cannot be null" );
120         return this;
121     }
122 
123     public VersionRangeResult resolveVersionRange( RepositorySystemSession session, VersionRangeRequest request )
124         throws VersionRangeResolutionException
125     {
126         VersionRangeResult result = new VersionRangeResult( request );
127 
128         VersionScheme versionScheme = new GenericVersionScheme();
129 
130         VersionConstraint versionConstraint;
131         try
132         {
133             versionConstraint = versionScheme.parseVersionConstraint( request.getArtifact().getVersion() );
134         }
135         catch ( InvalidVersionSpecificationException e )
136         {
137             result.addException( e );
138             throw new VersionRangeResolutionException( result );
139         }
140 
141         result.setVersionConstraint( versionConstraint );
142 
143         if ( versionConstraint.getRange() == null )
144         {
145             result.addVersion( versionConstraint.getVersion() );
146         }
147         else
148         {
149             Map<String, ArtifactRepository> versionIndex = getVersions( session, result, request );
150 
151             List<Version> versions = new ArrayList<>();
152             for ( Map.Entry<String, ArtifactRepository> v : versionIndex.entrySet() )
153             {
154                 try
155                 {
156                     Version ver = versionScheme.parseVersion( v.getKey() );
157                     if ( versionConstraint.containsVersion( ver ) )
158                     {
159                         versions.add( ver );
160                         result.setRepository( ver, v.getValue() );
161                     }
162                 }
163                 catch ( InvalidVersionSpecificationException e )
164                 {
165                     result.addException( e );
166                 }
167             }
168 
169             Collections.sort( versions );
170             result.setVersions( versions );
171         }
172 
173         return result;
174     }
175 
176     private Map<String, ArtifactRepository> getVersions( RepositorySystemSession session, VersionRangeResult result,
177                                                          VersionRangeRequest request )
178     {
179         RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
180 
181         Map<String, ArtifactRepository> versionIndex = new HashMap<>();
182 
183         Metadata metadata =
184             new DefaultMetadata( request.getArtifact().getGroupId(), request.getArtifact().getArtifactId(),
185                                  MAVEN_METADATA_XML, Metadata.Nature.RELEASE_OR_SNAPSHOT );
186 
187         List<MetadataRequest> metadataRequests = new ArrayList<>( request.getRepositories().size() );
188 
189         metadataRequests.add( new MetadataRequest( metadata, null, request.getRequestContext() ) );
190 
191         for ( RemoteRepository repository : request.getRepositories() )
192         {
193             MetadataRequest metadataRequest = new MetadataRequest( metadata, repository, request.getRequestContext() );
194             metadataRequest.setDeleteLocalCopyIfMissing( true );
195             metadataRequest.setTrace( trace );
196             metadataRequests.add( metadataRequest );
197         }
198 
199         List<MetadataResult> metadataResults = metadataResolver.resolveMetadata( session, metadataRequests );
200 
201         WorkspaceReader workspace = session.getWorkspaceReader();
202         if ( workspace != null )
203         {
204             List<String> versions = workspace.findVersions( request.getArtifact() );
205             for ( String version : versions )
206             {
207                 versionIndex.put( version, workspace.getRepository() );
208             }
209         }
210 
211         for ( MetadataResult metadataResult : metadataResults )
212         {
213             result.addException( metadataResult.getException() );
214 
215             ArtifactRepository repository = metadataResult.getRequest().getRepository();
216             if ( repository == null )
217             {
218                 repository = session.getLocalRepository();
219             }
220 
221             Versioning versioning = readVersions( session, trace, metadataResult.getMetadata(), repository, result );
222 
223             versioning = filterVersionsByRepositoryType( versioning, metadataResult.getRequest().getRepository() );
224 
225             for ( String version : versioning.getVersions() )
226             {
227                 if ( !versionIndex.containsKey( version ) )
228                 {
229                     versionIndex.put( version, repository );
230                 }
231             }
232         }
233 
234         return versionIndex;
235     }
236 
237     private Versioning readVersions( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
238                                      ArtifactRepository repository, VersionRangeResult result )
239     {
240         Versioning versioning = null;
241         try
242         {
243             if ( metadata != null )
244             {
245                 try ( SyncContext syncContext = syncContextFactory.newInstance( session, true ) )
246                 {
247                     syncContext.acquire( null, Collections.singleton( metadata ) );
248 
249                     if ( metadata.getFile() != null && metadata.getFile().exists() )
250                     {
251                         try ( final InputStream in = new FileInputStream( metadata.getFile() ) )
252                         {
253                             versioning = new MetadataXpp3Reader().read( in, false ).getVersioning();
254                         }
255                     }
256                 }
257             }
258         }
259         catch ( Exception e )
260         {
261             invalidMetadata( session, trace, metadata, repository, e );
262             result.addException( e );
263         }
264 
265         return ( versioning != null ) ? versioning : new Versioning();
266     }
267 
268     private Versioning filterVersionsByRepositoryType( Versioning versioning, RemoteRepository remoteRepository )
269     {
270         if ( remoteRepository == null )
271         {
272             return versioning;
273         }
274 
275         Versioning filteredVersions = versioning.clone();
276 
277         for ( String version : versioning.getVersions() )
278         {
279             if ( !remoteRepository.getPolicy( ArtifactUtils.isSnapshot( version ) ).isEnabled() )
280             {
281                 filteredVersions.removeVersion( version );
282             }
283         }
284 
285         return filteredVersions;
286     }
287 
288     private void invalidMetadata( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
289                                   ArtifactRepository repository, Exception exception )
290     {
291         RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INVALID );
292         event.setTrace( trace );
293         event.setMetadata( metadata );
294         event.setException( exception );
295         event.setRepository( repository );
296 
297         repositoryEventDispatcher.dispatch( event.build() );
298     }
299 
300 }