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