1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 16:28:12 -07:00

Extract RepoFinderCallable.

This commit is contained in:
Cedric Beust 2016-03-21 22:59:55 +04:00
parent 44f901ff04
commit a3afc91f4d
3 changed files with 189 additions and 180 deletions

View file

@ -2,7 +2,6 @@ package com.beust.kobalt.maven
import com.beust.kobalt.HostConfig import com.beust.kobalt.HostConfig
import com.beust.kobalt.api.Kobalt import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.maven.dependency.FileDependency
import com.beust.kobalt.misc.KobaltExecutors import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.Version import com.beust.kobalt.misc.Version
import com.beust.kobalt.misc.log import com.beust.kobalt.misc.log
@ -10,23 +9,15 @@ import com.beust.kobalt.misc.warn
import com.google.common.cache.CacheBuilder import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache import com.google.common.cache.LoadingCache
import kotlinx.dom.asElementList
import kotlinx.dom.parseXml
import org.w3c.dom.NodeList
import java.io.File
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorCompletionService import java.util.concurrent.ExecutorCompletionService
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory
/** /**
* Find the repo that contains the given dependency among a list of repos. Searches are performed in parallel and * Find the repo that contains the given dependency among a list of repos. Searches are performed in parallel and
* cached so we never make a network call for the same dependency more than once. * cached so we never make a network call for the same dependency more than once.
*/ */
class RepoFinder @Inject constructor(val executors: KobaltExecutors, val localRepo: LocalRepo, class RepoFinder @Inject constructor(val executors: KobaltExecutors, val finderFactory: RepoFinderCallable.IFactory) {
val pomFactory: Pom.IFactory) {
fun findCorrectRepo(id: String) = FOUND_REPOS.get(id) fun findCorrectRepo(id: String) = FOUND_REPOS.get(id)
/** /**
@ -66,7 +57,7 @@ class RepoFinder @Inject constructor(val executors: KobaltExecutors, val localRe
val results = arrayListOf<RepoResult>() val results = arrayListOf<RepoResult>()
try { try {
log(2, "Looking for $id") log(2, "Looking for $id")
Kobalt.repos.forEach { cs.submit(RepoFinderCallable(id, it)) } Kobalt.repos.forEach { cs.submit(finderFactory.create(id, it)) }
for (i in 0..Kobalt.repos.size - 1) { for (i in 0..Kobalt.repos.size - 1) {
try { try {
val repos = cs.take().get(2000, TimeUnit.MILLISECONDS) val repos = cs.take().get(2000, TimeUnit.MILLISECONDS)
@ -94,169 +85,4 @@ class RepoFinder @Inject constructor(val executors: KobaltExecutors, val localRe
return RepoResult(HostConfig(""), Version.of(id)) return RepoResult(HostConfig(""), Version.of(id))
} }
} }
/**
* Execute a single HTTP request to one repo. This Callable can return more than one RepoResult
* if the artifact we're tying to locate is a container pom (in which case, we'll return one
* positive RepoResult for each of the artifacts listed in that .pom file). For example:
* http://repo1.maven.org/maven2/nl/komponents/kovenant/kovenant/3.0.0/
*/
inner class RepoFinderCallable(val id: String, val repo: HostConfig) : Callable<List<RepoResult>> {
override fun call(): List<RepoResult> {
val repoUrl = repo.url
log(2, " Checking $repoUrl for $id")
val mavenId = MavenId.create(id)
val groupId = mavenId.groupId
val artifactId = mavenId.artifactId
if (mavenId.version == null) {
val ud = UnversionedDep(groupId, artifactId)
val isLocal = repoUrl.startsWith(FileDependency.PREFIX_FILE)
val path = ud.toMetadataXmlPath(false, isLocal)
val foundVersion = findCorrectVersionRelease(path, repoUrl)
// When looking up a versionless id, never return a SNAPSHOT
if (foundVersion != null && ! foundVersion.contains("SNAPSHOT")) {
return listOf(RepoResult(repo, Version.of(foundVersion), repoUrl + path))
} else {
return listOf(RepoResult(repo))
}
} else {
val version = Version.of(mavenId.version)
if (version.isSnapshot()) {
val dep = SimpleDep(mavenId)
val isLocal = repoUrl.startsWith(FileDependency.PREFIX_FILE)
val metadataXmlPath = dep.toMetadataXmlPath(false, isLocal, version.version)
val snapshotVersion =
if (isLocal) version
else findSnapshotVersion(metadataXmlPath, repoUrl, mavenId.version)
if (snapshotVersion != null) {
val url = repoUrl + dep.toDirectory(fileSystem = false, v = dep.version) +
dep.artifactId + "-" + snapshotVersion.noSnapshotVersion +
"-" + snapshotVersion.snapshotTimestamp + ".jar"
return listOf(RepoResult(repo, version, url, snapshotVersion))
} else {
return listOf(RepoResult(repo))
}
} else if (version.isRangedVersion() ) {
val foundVersion = findRangedVersion(SimpleDep(mavenId), repoUrl)
if (foundVersion != null) {
return listOf(RepoResult(repo, foundVersion))
} else {
return listOf(RepoResult(repo))
}
} else {
val dep = SimpleDep(mavenId)
// Try to find the jar file
val depPomFile = dep.toPomFile(dep.version)
val attemptPaths = listOf(dep.toJarFile(dep.version), dep.toAarFile(dep.version), depPomFile)
val attemptUrls = attemptPaths.map { repo.copy(url = repo.url + it )} +
attemptPaths.map { repo.copy(url = repo.url + File(it).parentFile.path.replace("\\", "/")) }
val firstFound = attemptUrls.map { Kurl(it)}.firstOrNull { it.exists }
if (firstFound != null) {
val url = firstFound.hostInfo.url
if (url.endsWith("ar")) {
log(3, "Result for $repoUrl for $id: $firstFound")
return listOf(RepoResult(repo, Version.of(dep.version), firstFound.hostInfo.url))
} else if (url.endsWith(".pom")) {
log(2, "Found container pom: " + firstFound)
File(localRepo.toFullPath(depPomFile)).let { pomFile ->
pomFile.parentFile.mkdirs()
Kurl(HostConfig(url)).toFile(pomFile)
val dependencies = pomFactory.create(id, pomFile).dependencies
val result = arrayListOf<RepoResult>()
dependencies.map { it.id }.forEach {
result.addAll(RepoFinderCallable(it, repo).call())
}
return result
}
} else {
return listOf(RepoResult(repo, Version.of(dep.version), firstFound.hostInfo.url))
}
} else {
log(3, "Couldn't find $dep on $repoUrl")
return emptyList()
}
}
}
}
}
val XPATH_FACTORY = XPathFactory.newInstance();
val XPATH = XPATH_FACTORY.newXPath();
fun findCorrectVersionRelease(metadataPath: String, repoUrl: String): String? {
val XPATHS = listOf(
XPATH.compile("/metadata/version"),
XPATH.compile("/metadata/versioning/latest"),
XPATH.compile("/metadata/versioning/release"))
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
arrayListOf(XPATHS.forEach {
val result = it.evaluate(doc, XPathConstants.STRING) as String
if (! result.isEmpty()) {
return result
}
})
} catch(ex: Exception) {
log(2, "Couldn't find metadata at $url: ${ex.message}")
}
return null
}
fun findRangedVersion(dep: SimpleDep, repoUrl: String): Version? {
val l = listOf(dep.groupId.replace(".", "/"), dep.artifactId.replace(".", "/"), "maven-metadata.xml")
var metadataPath = l.joinToString("/")
val versionsXpath = XPATH.compile("/metadata/versioning/versions/version")
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
val version = Version.of(dep.version)
if(version.isRangedVersion()) {
val versions = (versionsXpath.evaluate(doc, XPathConstants.NODESET) as NodeList)
.asElementList().map { Version.of(it.textContent) }
return version.select(versions)
} else {
return Version.of(XPATH.compile("/metadata/versioning/versions/version/$version")
.evaluate(doc, XPathConstants.STRING) as String)
}
} catch(ex: Exception) {
log(2, "Couldn't find metadata at ${url}")
}
return null
}
fun findSnapshotVersion(metadataPath: String, repoUrl: String, snapshotVersion: String): Version? {
val timestamp = XPATH.compile("/metadata/versioning/snapshot/timestamp")
val buildNumber = XPATH.compile("/metadata/versioning/snapshot/buildNumber")
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
val ts = timestamp.evaluate(doc, XPathConstants.STRING)
val bn = buildNumber.evaluate(doc, XPathConstants.STRING)
if (! ts.toString().isEmpty() && ! bn.toString().isEmpty()) {
return Version(snapshotVersion, ts.toString() + "-" + bn.toString())
} else {
val lastUpdated = XPATH.compile("/metadata/versioning/lastUpdated")
if (! lastUpdated.toString().isEmpty()) {
return Version.of(lastUpdated.toString())
}
}
} catch(ex: Exception) {
log(2, "Couldn't find metadata at $url")
}
return null
}
} }

View file

@ -0,0 +1,185 @@
package com.beust.kobalt.maven
import com.beust.kobalt.HostConfig
import com.beust.kobalt.maven.dependency.FileDependency
import com.beust.kobalt.misc.Version
import com.beust.kobalt.misc.log
import com.google.inject.Inject
import com.google.inject.assistedinject.Assisted
import kotlinx.dom.asElementList
import kotlinx.dom.parseXml
import org.w3c.dom.NodeList
import java.io.File
import java.util.concurrent.Callable
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory
/**
* Execute a single HTTP request to one repo. This Callable can return more than one RepoResult
* if the artifact we're tying to locate is a container pom (in which case, we'll return one
* positive RepoResult for each of the artifacts listed in that .pom file). For example:
* http://repo1.maven.org/maven2/nl/komponents/kovenant/kovenant/3.0.0/
*/
class RepoFinderCallable @Inject constructor(@Assisted val id: String,
@Assisted val repo: HostConfig, val localRepo: LocalRepo, val pomFactory: Pom.IFactory)
: Callable<List<RepoFinder .RepoResult>> {
interface IFactory {
fun create(@Assisted id: String, @Assisted repo: HostConfig) : RepoFinderCallable
}
override fun call(): List<RepoFinder.RepoResult> {
val repoUrl = repo.url
log(2, " Checking $repoUrl for $id")
val mavenId = MavenId.create(id)
val groupId = mavenId.groupId
val artifactId = mavenId.artifactId
if (mavenId.version == null) {
val ud = UnversionedDep(groupId, artifactId)
val isLocal = repoUrl.startsWith(FileDependency.PREFIX_FILE)
val path = ud.toMetadataXmlPath(false, isLocal)
val foundVersion = findCorrectVersionRelease(path, repoUrl)
// When looking up a versionless id, never return a SNAPSHOT
if (foundVersion != null && ! foundVersion.contains("SNAPSHOT")) {
return listOf(RepoFinder.RepoResult(repo, Version.of(foundVersion), repoUrl + path))
} else {
return listOf(RepoFinder.RepoResult(repo))
}
} else {
val version = Version.of(mavenId.version)
if (version.isSnapshot()) {
val dep = SimpleDep(mavenId)
val isLocal = repoUrl.startsWith(FileDependency.PREFIX_FILE)
val metadataXmlPath = dep.toMetadataXmlPath(false, isLocal, version.version)
val snapshotVersion =
if (isLocal) version
else findSnapshotVersion(metadataXmlPath, repoUrl, mavenId.version)
if (snapshotVersion != null) {
val url = repoUrl + dep.toDirectory(fileSystem = false, v = dep.version) +
dep.artifactId + "-" + snapshotVersion.noSnapshotVersion +
"-" + snapshotVersion.snapshotTimestamp + ".jar"
return listOf(RepoFinder.RepoResult(repo, version, url, snapshotVersion))
} else {
return listOf(RepoFinder.RepoResult(repo))
}
} else if (version.isRangedVersion() ) {
val foundVersion = findRangedVersion(SimpleDep(mavenId), repoUrl)
if (foundVersion != null) {
return listOf(RepoFinder.RepoResult(repo, foundVersion))
} else {
return listOf(RepoFinder.RepoResult(repo))
}
} else {
val dep = SimpleDep(mavenId)
// Try to find the jar file
val depPomFile = dep.toPomFile(dep.version)
val attemptPaths = listOf(dep.toJarFile(dep.version), dep.toAarFile(dep.version), depPomFile)
val attemptUrls = attemptPaths.map { repo.copy(url = repo.url + it )} +
attemptPaths.map { repo.copy(url = repo.url + File(it).parentFile.path.replace("\\", "/")) }
val firstFound = attemptUrls.map { Kurl(it)}.firstOrNull { it.exists }
if (firstFound != null) {
val url = firstFound.hostInfo.url
if (url.endsWith("ar")) {
log(3, "Result for $repoUrl for $id: $firstFound")
return listOf(RepoFinder.RepoResult(repo, Version.of(dep.version), firstFound.hostInfo.url))
} else if (url.endsWith(".pom")) {
log(2, "Found container pom: " + firstFound)
File(localRepo.toFullPath(depPomFile)).let { pomFile ->
pomFile.parentFile.mkdirs()
Kurl(HostConfig(url)).toFile(pomFile)
val dependencies = pomFactory.create(id, pomFile).dependencies
val result = arrayListOf<RepoFinder.RepoResult>()
dependencies.map { it.id }.forEach {
result.addAll(RepoFinderCallable(it, repo, localRepo, pomFactory).call())
}
return result
}
} else {
return listOf(RepoFinder.RepoResult(repo, Version.of(dep.version), firstFound.hostInfo.url))
}
} else {
log(3, "Couldn't find $dep on $repoUrl")
return emptyList()
}
}
}
}
val XPATH_FACTORY = XPathFactory.newInstance();
val XPATH = XPATH_FACTORY.newXPath();
private fun findCorrectVersionRelease(metadataPath: String, repoUrl: String): String? {
val XPATHS = listOf(
XPATH.compile("/metadata/version"),
XPATH.compile("/metadata/versioning/latest"),
XPATH.compile("/metadata/versioning/release"))
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
arrayListOf(XPATHS.forEach {
val result = it.evaluate(doc, XPathConstants.STRING) as String
if (! result.isEmpty()) {
return result
}
})
} catch(ex: Exception) {
log(2, "Couldn't find metadata at $url: ${ex.message}")
}
return null
}
fun findRangedVersion(dep: SimpleDep, repoUrl: String): Version? {
val l = listOf(dep.groupId.replace(".", "/"), dep.artifactId.replace(".", "/"), "maven-metadata.xml")
var metadataPath = l.joinToString("/")
val versionsXpath = XPATH.compile("/metadata/versioning/versions/version")
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
val version = Version.of(dep.version)
if(version.isRangedVersion()) {
val versions = (versionsXpath.evaluate(doc, XPathConstants.NODESET) as NodeList)
.asElementList().map { Version.of(it.textContent) }
return version.select(versions)
} else {
return Version.of(XPATH.compile("/metadata/versioning/versions/version/$version")
.evaluate(doc, XPathConstants.STRING) as String)
}
} catch(ex: Exception) {
log(2, "Couldn't find metadata at ${url}")
}
return null
}
fun findSnapshotVersion(metadataPath: String, repoUrl: String, snapshotVersion: String): Version? {
val timestamp = XPATH.compile("/metadata/versioning/snapshot/timestamp")
val buildNumber = XPATH.compile("/metadata/versioning/snapshot/buildNumber")
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
val ts = timestamp.evaluate(doc, XPathConstants.STRING)
val bn = buildNumber.evaluate(doc, XPathConstants.STRING)
if (! ts.toString().isEmpty() && ! bn.toString().isEmpty()) {
return Version(snapshotVersion, ts.toString() + "-" + bn.toString())
} else {
val lastUpdated = XPATH.compile("/metadata/versioning/lastUpdated")
if (! lastUpdated.toString().isEmpty()) {
return Version.of(lastUpdated.toString())
}
}
} catch(ex: Exception) {
log(2, "Couldn't find metadata at $url")
}
return null
}
}

View file

@ -3,10 +3,7 @@ package com.beust.kobalt.app
import com.beust.kobalt.Args import com.beust.kobalt.Args
import com.beust.kobalt.internal.KobaltSettings import com.beust.kobalt.internal.KobaltSettings
import com.beust.kobalt.internal.PluginInfo import com.beust.kobalt.internal.PluginInfo
import com.beust.kobalt.maven.ArtifactFetcher import com.beust.kobalt.maven.*
import com.beust.kobalt.maven.LocalRepo
import com.beust.kobalt.maven.Pom
import com.beust.kobalt.maven.PomGenerator
import com.beust.kobalt.maven.dependency.MavenDependency import com.beust.kobalt.maven.dependency.MavenDependency
import com.beust.kobalt.misc.DependencyExecutor import com.beust.kobalt.misc.DependencyExecutor
import com.beust.kobalt.misc.KobaltExecutors import com.beust.kobalt.misc.KobaltExecutors
@ -35,6 +32,7 @@ public open class MainModule(val args: Args, val settings: KobaltSettings) : Abs
Pom.IFactory::class.java, Pom.IFactory::class.java,
BuildFileCompiler.IFactory::class.java, BuildFileCompiler.IFactory::class.java,
ArtifactFetcher.IFactory::class.java, ArtifactFetcher.IFactory::class.java,
RepoFinderCallable.IFactory::class.java,
MavenDependency.IFactory::class.java) MavenDependency.IFactory::class.java)
.forEach { .forEach {
install(builder.build(it)) install(builder.build(it))