001package org.apache.maven.repository.internal; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.commons.lang3.Validate; 023import org.apache.maven.artifact.repository.metadata.Versioning; 024import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; 025import org.codehaus.plexus.component.annotations.Component; 026import org.codehaus.plexus.component.annotations.Requirement; 027import org.codehaus.plexus.util.IOUtil; 028import org.eclipse.aether.RepositoryEvent; 029import org.eclipse.aether.RepositoryEvent.EventType; 030import org.eclipse.aether.RepositorySystemSession; 031import org.eclipse.aether.RequestTrace; 032import org.eclipse.aether.SyncContext; 033import org.eclipse.aether.impl.MetadataResolver; 034import org.eclipse.aether.impl.RepositoryEventDispatcher; 035import org.eclipse.aether.impl.SyncContextFactory; 036import org.eclipse.aether.impl.VersionRangeResolver; 037import org.eclipse.aether.metadata.DefaultMetadata; 038import org.eclipse.aether.metadata.Metadata; 039import org.eclipse.aether.repository.ArtifactRepository; 040import org.eclipse.aether.repository.RemoteRepository; 041import org.eclipse.aether.repository.WorkspaceReader; 042import org.eclipse.aether.resolution.MetadataRequest; 043import org.eclipse.aether.resolution.MetadataResult; 044import org.eclipse.aether.resolution.VersionRangeRequest; 045import org.eclipse.aether.resolution.VersionRangeResolutionException; 046import org.eclipse.aether.resolution.VersionRangeResult; 047import org.eclipse.aether.spi.locator.Service; 048import org.eclipse.aether.spi.locator.ServiceLocator; 049import org.eclipse.aether.spi.log.Logger; 050import org.eclipse.aether.spi.log.LoggerFactory; 051import org.eclipse.aether.spi.log.NullLoggerFactory; 052import org.eclipse.aether.util.version.GenericVersionScheme; 053import org.eclipse.aether.version.InvalidVersionSpecificationException; 054import org.eclipse.aether.version.Version; 055import org.eclipse.aether.version.VersionConstraint; 056import org.eclipse.aether.version.VersionScheme; 057 058import javax.inject.Inject; 059import javax.inject.Named; 060import java.io.FileInputStream; 061import java.util.ArrayList; 062import java.util.Collections; 063import java.util.HashMap; 064import java.util.List; 065import java.util.Map; 066 067/** 068 * @author Benjamin Bentmann 069 */ 070@Named 071@Component( role = VersionRangeResolver.class ) 072public class DefaultVersionRangeResolver 073 implements VersionRangeResolver, Service 074{ 075 076 private static final String MAVEN_METADATA_XML = "maven-metadata.xml"; 077 078 @SuppressWarnings( "unused" ) 079 @Requirement( role = LoggerFactory.class ) 080 private Logger logger = NullLoggerFactory.LOGGER; 081 082 @Requirement 083 private MetadataResolver metadataResolver; 084 085 @Requirement 086 private SyncContextFactory syncContextFactory; 087 088 @Requirement 089 private RepositoryEventDispatcher repositoryEventDispatcher; 090 091 public DefaultVersionRangeResolver() 092 { 093 // enable default constructor 094 } 095 096 @Inject 097 DefaultVersionRangeResolver( MetadataResolver metadataResolver, SyncContextFactory syncContextFactory, 098 RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory ) 099 { 100 setMetadataResolver( metadataResolver ); 101 setSyncContextFactory( syncContextFactory ); 102 setLoggerFactory( loggerFactory ); 103 setRepositoryEventDispatcher( repositoryEventDispatcher ); 104 } 105 106 public void initService( ServiceLocator locator ) 107 { 108 setLoggerFactory( locator.getService( LoggerFactory.class ) ); 109 setMetadataResolver( locator.getService( MetadataResolver.class ) ); 110 setSyncContextFactory( locator.getService( SyncContextFactory.class ) ); 111 setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) ); 112 } 113 114 public DefaultVersionRangeResolver setLoggerFactory( LoggerFactory loggerFactory ) 115 { 116 this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() ); 117 return this; 118 } 119 120 void setLogger( LoggerFactory loggerFactory ) 121 { 122 // plexus support 123 setLoggerFactory( loggerFactory ); 124 } 125 126 public DefaultVersionRangeResolver setMetadataResolver( MetadataResolver metadataResolver ) 127 { 128 this.metadataResolver = Validate.notNull( metadataResolver, "metadataResolver cannot be null" ); 129 return this; 130 } 131 132 public DefaultVersionRangeResolver setSyncContextFactory( SyncContextFactory syncContextFactory ) 133 { 134 this.syncContextFactory = Validate.notNull( syncContextFactory, "syncContextFactory cannot be null" ); 135 return this; 136 } 137 138 public DefaultVersionRangeResolver setRepositoryEventDispatcher( 139 RepositoryEventDispatcher repositoryEventDispatcher ) 140 { 141 this.repositoryEventDispatcher = Validate.notNull( repositoryEventDispatcher, 142 "repositoryEventDispatcher cannot be null" ); 143 return this; 144 } 145 146 public VersionRangeResult resolveVersionRange( RepositorySystemSession session, VersionRangeRequest request ) 147 throws VersionRangeResolutionException 148 { 149 VersionRangeResult result = new VersionRangeResult( request ); 150 151 VersionScheme versionScheme = new GenericVersionScheme(); 152 153 VersionConstraint versionConstraint; 154 try 155 { 156 versionConstraint = versionScheme.parseVersionConstraint( request.getArtifact().getVersion() ); 157 } 158 catch ( InvalidVersionSpecificationException e ) 159 { 160 result.addException( e ); 161 throw new VersionRangeResolutionException( result ); 162 } 163 164 result.setVersionConstraint( versionConstraint ); 165 166 if ( versionConstraint.getRange() == null ) 167 { 168 result.addVersion( versionConstraint.getVersion() ); 169 } 170 else 171 { 172 Map<String, ArtifactRepository> versionIndex = getVersions( session, result, request ); 173 174 List<Version> versions = new ArrayList<>(); 175 for ( Map.Entry<String, ArtifactRepository> v : versionIndex.entrySet() ) 176 { 177 try 178 { 179 Version ver = versionScheme.parseVersion( v.getKey() ); 180 if ( versionConstraint.containsVersion( ver ) ) 181 { 182 versions.add( ver ); 183 result.setRepository( ver, v.getValue() ); 184 } 185 } 186 catch ( InvalidVersionSpecificationException e ) 187 { 188 result.addException( e ); 189 } 190 } 191 192 Collections.sort( versions ); 193 result.setVersions( versions ); 194 } 195 196 return result; 197 } 198 199 private Map<String, ArtifactRepository> getVersions( RepositorySystemSession session, VersionRangeResult result, 200 VersionRangeRequest request ) 201 { 202 RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); 203 204 Map<String, ArtifactRepository> versionIndex = new HashMap<>(); 205 206 Metadata metadata = 207 new DefaultMetadata( request.getArtifact().getGroupId(), request.getArtifact().getArtifactId(), 208 MAVEN_METADATA_XML, Metadata.Nature.RELEASE_OR_SNAPSHOT ); 209 210 List<MetadataRequest> metadataRequests = new ArrayList<>( request.getRepositories().size() ); 211 212 metadataRequests.add( new MetadataRequest( metadata, null, request.getRequestContext() ) ); 213 214 for ( RemoteRepository repository : request.getRepositories() ) 215 { 216 MetadataRequest metadataRequest = new MetadataRequest( metadata, repository, request.getRequestContext() ); 217 metadataRequest.setDeleteLocalCopyIfMissing( true ); 218 metadataRequest.setTrace( trace ); 219 metadataRequests.add( metadataRequest ); 220 } 221 222 List<MetadataResult> metadataResults = metadataResolver.resolveMetadata( session, metadataRequests ); 223 224 WorkspaceReader workspace = session.getWorkspaceReader(); 225 if ( workspace != null ) 226 { 227 List<String> versions = workspace.findVersions( request.getArtifact() ); 228 for ( String version : versions ) 229 { 230 versionIndex.put( version, workspace.getRepository() ); 231 } 232 } 233 234 for ( MetadataResult metadataResult : metadataResults ) 235 { 236 result.addException( metadataResult.getException() ); 237 238 ArtifactRepository repository = metadataResult.getRequest().getRepository(); 239 if ( repository == null ) 240 { 241 repository = session.getLocalRepository(); 242 } 243 244 Versioning versioning = readVersions( session, trace, metadataResult.getMetadata(), repository, result ); 245 for ( String version : versioning.getVersions() ) 246 { 247 if ( !versionIndex.containsKey( version ) ) 248 { 249 versionIndex.put( version, repository ); 250 } 251 } 252 } 253 254 return versionIndex; 255 } 256 257 private Versioning readVersions( RepositorySystemSession session, RequestTrace trace, Metadata metadata, 258 ArtifactRepository repository, VersionRangeResult result ) 259 { 260 Versioning versioning = null; 261 262 FileInputStream fis = null; 263 try 264 { 265 if ( metadata != null ) 266 { 267 268 try ( SyncContext syncContext = syncContextFactory.newInstance( session, true ) ) 269 { 270 syncContext.acquire( null, Collections.singleton( metadata ) ); 271 272 if ( metadata.getFile() != null && metadata.getFile().exists() ) 273 { 274 fis = new FileInputStream( metadata.getFile() ); 275 org.apache.maven.artifact.repository.metadata.Metadata m = 276 new MetadataXpp3Reader().read( fis, false ); 277 versioning = m.getVersioning(); 278 } 279 } 280 } 281 } 282 catch ( Exception e ) 283 { 284 invalidMetadata( session, trace, metadata, repository, e ); 285 result.addException( e ); 286 } 287 finally 288 { 289 IOUtil.close( fis ); 290 } 291 292 return ( versioning != null ) ? versioning : new Versioning(); 293 } 294 295 private void invalidMetadata( RepositorySystemSession session, RequestTrace trace, Metadata metadata, 296 ArtifactRepository repository, Exception exception ) 297 { 298 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INVALID ); 299 event.setTrace( trace ); 300 event.setMetadata( metadata ); 301 event.setException( exception ); 302 event.setRepository( repository ); 303 304 repositoryEventDispatcher.dispatch( event.build() ); 305 } 306 307}