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