Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve resolving of maven based dependencies #7981

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 64 additions & 97 deletions plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,16 @@
import org.eclipse.aether.RepositorySystemSession
import org.eclipse.aether.artifact.Artifact
import org.eclipse.aether.artifact.DefaultArtifact
import org.eclipse.aether.impl.RemoteRepositoryManager
import org.eclipse.aether.impl.RepositoryConnectorProvider
import org.eclipse.aether.repository.MirrorSelector
import org.eclipse.aether.repository.RemoteRepository
import org.eclipse.aether.repository.WorkspaceReader
import org.eclipse.aether.resolution.ArtifactDescriptorRequest
import org.eclipse.aether.spi.connector.ArtifactDownload
import org.eclipse.aether.resolution.ArtifactRequest
import org.eclipse.aether.spi.connector.layout.RepositoryLayout
import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider
import org.eclipse.aether.spi.connector.transport.GetTask
import org.eclipse.aether.spi.connector.transport.PeekTask
import org.eclipse.aether.spi.connector.transport.TransporterProvider
import org.eclipse.aether.transfer.AbstractTransferListener
import org.eclipse.aether.transfer.NoRepositoryConnectorException
import org.eclipse.aether.transfer.TransferEvent
import org.eclipse.aether.util.repository.JreProxySelector

import org.ossreviewtoolkit.analyzer.PackageManager
Expand Down Expand Up @@ -499,44 +495,16 @@

private fun requestRemoteArtifact(
artifact: Artifact,
repositories: List<RemoteRepository>,
useReposFromDependencies: Boolean
artifactRepository: RemoteRepository,
allRepositories: List<RemoteRepository>,
): RemoteArtifact {
val allRepositories = if (useReposFromDependencies) {
val repoSystem = containerLookup<RepositorySystem>()

// Create an artifact descriptor to get the list of repositories from the related POM file.
val artifactDescriptorRequest = ArtifactDescriptorRequest(artifact, repositories, "project")
val artifactDescriptorResult = repoSystem
.readArtifactDescriptor(repositorySystemSession, artifactDescriptorRequest)
repositories + artifactDescriptorResult.repositories
} else {
repositories
}.toSet()

val cacheKey = "$artifact@$allRepositories"

remoteArtifactCache.read(cacheKey)?.let {
logger.debug { "Reading remote artifact for '$artifact' from disk cache." }
return it.fromYaml()
}

// Filter out local repositories, as remote artifacts should never point to files on the local disk.
val remoteRepositories = allRepositories.filterNot {
// Some (Linux) file URIs do not start with "file://" but look like "file:/opt/android-sdk-linux".
it.url.startsWith("file:/")
}.map { repository ->
val proxy = repositorySystemSession.proxySelector.getProxy(repository)
val authentication = repositorySystemSession.authenticationSelector.getAuthentication(repository)
RemoteRepository.Builder(repository).setAuthentication(authentication).setProxy(proxy).build()
}.toSet()

if (allRepositories.size > remoteRepositories.size) {
logger.debug { "Ignoring local repositories ${allRepositories - remoteRepositories}." }
}

logger.debug { "Searching for '$artifact' in $remoteRepositories." }

data class ArtifactLocationInfo(
val repository: RemoteRepository,
val layout: RepositoryLayout,
Expand All @@ -546,7 +514,7 @@

val repositoryLayoutProvider = containerLookup<RepositoryLayoutProvider>()

val locationInfo = remoteRepositories.mapNotNull { repository ->
val locationInfo = listOf(artifactRepository).mapNotNull { repository ->

Check warning on line 517 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L517

Added line #L517 was not covered by tests
val repositoryLayout = runCatching {
repositoryLayoutProvider.newRepositoryLayout(repositorySystemSession, repository)
}.onFailure {
Expand All @@ -562,82 +530,54 @@
}
}

val remoteRepositoryManager = containerLookup<RemoteRepositoryManager>()
val repositoryConnectorProvider = containerLookup<RepositoryConnectorProvider>()
val transporterProvider = containerLookup<TransporterProvider>()

// Check the remote repositories for the availability of the artifact.
// TODO: Currently only the first hit is stored, could query the rest of the repositories if required.
locationInfo.forEach { info ->
logger.debug { "Trying to download artifact '$artifact' from ${info.downloadUrl}." }

val snapshot = artifact.isSnapshot
val policy = remoteRepositoryManager.getPolicy(
repositorySystemSession, info.repository, !snapshot, snapshot
)

val localPath = repositorySystemSession.localRepositoryManager
.getPathForRemoteArtifact(artifact, info.repository, "project")
val downloadFile = File(repositorySystemSession.localRepositoryManager.repository.basedir, localPath)

val artifactDownload = ArtifactDownload(artifact, "project", downloadFile, policy.checksumPolicy)
artifactDownload.isExistenceCheck = true
artifactDownload.listener = object : AbstractTransferListener() {
override fun transferFailed(event: TransferEvent?) {
logger.debug {
"Transfer failed for repository with ID '${info.repository.id}': $event"
}
}
val checksumLocations = info.layout.getChecksumLocations(artifact, false, info.location)

Check warning on line 540 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L540

Added line #L540 was not covered by tests

override fun transferSucceeded(event: TransferEvent?) {
logger.debug { "Transfer succeeded: $event" }
}
}
// TODO: Could store multiple checksums in model instead of only the first.
val checksumLocation = checksumLocations.first()

Check warning on line 543 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L543

Added line #L543 was not covered by tests

try {
wrapMavenSession {
repositoryConnectorProvider.newRepositoryConnector(repositorySystemSession, info.repository).use {
it.get(listOf(artifactDownload), null)
}
}
} catch (e: NoRepositoryConnectorException) {
e.showStackTrace()
logger.info { "Getting checksum for '$artifact' in '${info.repository}' ${checksumLocation}." }

Check warning on line 545 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L545

Added line #L545 was not covered by tests

logger.warn { "Could not create connector for repository '${info.repository}': ${e.collectMessages()}" }
val transporter = transporterProvider.newTransporter(repositorySystemSession, info.repository)

Check warning on line 547 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L547

Added line #L547 was not covered by tests

return@forEach
}
val hash = runCatching {
val task = GetTask(checksumLocation.location)
transporter.get(task)

Check warning on line 551 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L549-L551

Added lines #L549 - L551 were not covered by tests

if (artifactDownload.exception == null) {
logger.debug { "Found '$artifact' in '${info.repository}'." }

val checksumLocations = info.layout.getChecksumLocations(artifact, false, info.location)

// TODO: Could store multiple checksums in model instead of only the first.
val checksumLocation = checksumLocations.first()
parseChecksum(task.dataString, checksumLocation.checksumAlgorithmFactory.name)

Check warning on line 553 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L553

Added line #L553 was not covered by tests
}.getOrElse {
it.showStackTrace()

Check warning on line 555 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L555

Added line #L555 was not covered by tests

val transporter = transporterProvider.newTransporter(repositorySystemSession, info.repository)
logger.warn { "Could not get checksum for '$artifact': ${it.collectMessages()}" }

Check warning on line 557 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L557

Added line #L557 was not covered by tests

val hash = runCatching {
val task = GetTask(checksumLocation.location)
transporter.get(task)
// Fall back to an empty hash.
Hash.NONE

Check warning on line 560 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L560

Added line #L560 was not covered by tests
}

parseChecksum(task.dataString, checksumLocation.checksumAlgorithmFactory.name)
if (hash == Hash.NONE) {
val artifactFound = runCatching {
val task = PeekTask(info.location)
transporter.peek(task)
true

Check warning on line 567 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L564-L567

Added lines #L564 - L567 were not covered by tests
}.getOrElse {
it.showStackTrace()

logger.warn { "Could not get checksum for '$artifact': ${it.collectMessages()}" }

// Fall back to an empty hash.
Hash.NONE
logger.warn { "Could not get artifact '$artifact': ${it.collectMessages()}" }
false

Check warning on line 571 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L570-L571

Added lines #L570 - L571 were not covered by tests
}

return RemoteArtifact(info.downloadUrl, hash).also { remoteArtifact ->
logger.debug { "Writing remote artifact for '$artifact' to disk cache." }
remoteArtifactCache.write(cacheKey, remoteArtifact.toYaml())
if (!artifactFound) {
return@forEach

Check warning on line 575 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L575

Added line #L575 was not covered by tests
}
} else {
logger.debug { artifactDownload.exception.collectMessages() }
}
return RemoteArtifact(info.downloadUrl, hash).also { remoteArtifact ->
logger.debug { "Writing remote artifact for '$artifact' to disk cache." }
remoteArtifactCache.write(cacheKey, remoteArtifact.toYaml())

Check warning on line 580 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L578-L580

Added lines #L578 - L580 were not covered by tests
}
}

Expand Down Expand Up @@ -719,21 +659,48 @@
val declaredLicenses = parseLicenses(mavenProject)
val declaredLicensesProcessed = processDeclaredLicenses(declaredLicenses)

val binaryRemoteArtifact = localProject?.let {
RemoteArtifact.EMPTY
} ?: requestRemoteArtifact(artifact, repositories, useReposFromDependencies)
// retrieve the pom file for the artifact to know from which repository it is coming
val pomArtifact = artifact.let {
DefaultArtifact(it.groupId, it.artifactId, null, "pom", it.version)

Check warning on line 664 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L663-L664

Added lines #L663 - L664 were not covered by tests
}
val pomArtifactRequest = ArtifactRequest(pomArtifact, repositories, "project")
val result = containerLookup<RepositorySystem>().resolveArtifact(repositorySystemSession, pomArtifactRequest)
val artifactRepository = result.repository

Check warning on line 668 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L666-L668

Added lines #L666 - L668 were not covered by tests

// reconstruct all repositories that would be used for resolving the artifact
// the repositories are used to construct a cache key for the artifact
val allRepositories = if (useReposFromDependencies) {
val repoSystem = containerLookup<RepositorySystem>()

Check warning on line 673 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L673

Added line #L673 was not covered by tests

// Create an artifact descriptor to get the list of repositories from the related POM file.
val artifactDescriptorRequest = ArtifactDescriptorRequest(artifact, repositories, "project")
val artifactDescriptorResult = repoSystem
.readArtifactDescriptor(repositorySystemSession, artifactDescriptorRequest)
repositories + artifactDescriptorResult.repositories

Check warning on line 679 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L676-L679

Added lines #L676 - L679 were not covered by tests
} else {
repositories
}.toSet()

Check warning on line 682 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L681-L682

Added lines #L681 - L682 were not covered by tests

val binaryRemoteArtifact = when {

Check warning on line 684 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L684

Added line #L684 was not covered by tests
localProject != null -> RemoteArtifact.EMPTY
artifactRepository !is RemoteRepository -> RemoteArtifact.EMPTY
else -> {
requestRemoteArtifact(artifact, artifactRepository, allRepositories.toList())

Check warning on line 688 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L688

Added line #L688 was not covered by tests
}
}

val isBinaryArtifactModified = isArtifactModified(artifact, binaryRemoteArtifact)

val sourceRemoteArtifact = when {
localProject != null -> RemoteArtifact.EMPTY
artifactRepository !is RemoteRepository -> RemoteArtifact.EMPTY
artifact.extension == "pom" -> binaryRemoteArtifact
else -> {
val sourceArtifact = artifact.let {
DefaultArtifact(it.groupId, it.artifactId, "sources", "jar", it.version)
}

requestRemoteArtifact(sourceArtifact, repositories, useReposFromDependencies)
requestRemoteArtifact(sourceArtifact, artifactRepository, allRepositories.toList())

Check warning on line 703 in plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt

View check run for this annotation

Codecov / codecov/patch

plugins/package-managers/maven/src/main/kotlin/utils/MavenSupport.kt#L703

Added line #L703 was not covered by tests
}
}

Expand Down
Loading