Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions foojay-resolver/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ testing {
val functionalTest by registering(JvmTestSuite::class) {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-test-junit5")
implementation(libs.littleproxy)
}
}
val test by getting(JvmTestSuite::class) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package org.gradle.toolchains.foojay

import io.netty.handler.codec.http.HttpObject
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponse
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.io.TempDir
import org.littleshoot.proxy.HttpFilters
import org.littleshoot.proxy.HttpFiltersAdapter
import org.littleshoot.proxy.HttpFiltersSourceAdapter
import org.littleshoot.proxy.HttpProxyServer
import org.littleshoot.proxy.impl.DefaultHttpProxyServer
import java.io.File
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertTrue

abstract class AbstractFoojayToolchainsPluginFunctionalTest {
Expand All @@ -16,6 +26,10 @@ abstract class AbstractFoojayToolchainsPluginFunctionalTest {
@field:TempDir
protected lateinit var homeDir: File

protected lateinit var proxyServer: HttpProxyServer
protected var proxyPort: Int = 0
protected val proxyInterceptorCount = AtomicInteger(0)

private val settingsFile by lazy { projectDir.resolve("settings.gradle.kts") }
private val propertiesFile by lazy { projectDir.resolve("gradle.properties") }
private val buildFile by lazy { projectDir.resolve("build.gradle.kts") }
Expand All @@ -27,9 +41,38 @@ abstract class AbstractFoojayToolchainsPluginFunctionalTest {
org.gradle.java.installations.auto-detect=false
org.gradle.java.installations.auto-download=true
""".trimIndent())
// Start proxy on a random available port
proxyInterceptorCount.set(0)
proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(0)
.withFiltersSource(object : HttpFiltersSourceAdapter() {
override fun filterRequest(originalRequest: HttpRequest): HttpFilters {
return object : HttpFiltersAdapter(originalRequest) {
override fun clientToProxyRequest(httpObject: HttpObject): HttpResponse? {
// Increment whenever the proxy intercepts a request
if (httpObject is HttpRequest) {
proxyInterceptorCount.incrementAndGet()
}
return null // Continue normal routing
}
}
}
})
.start()
proxyPort = proxyServer.listenAddress.port
}

protected fun runner(settings: String, buildScript: String): GradleRunner {
@AfterEach
internal fun tearDownProxy() {
proxyServer.stop()
}

protected fun runner(
settings: String,
buildScript: String,
extraArguments: List<String> = emptyList()
): GradleRunner {

settingsFile.writeText(settings)
buildFile.writeText(buildScript.trimIndent())

Expand All @@ -46,7 +89,7 @@ abstract class AbstractFoojayToolchainsPluginFunctionalTest {
return GradleRunner.create()
.forwardOutput()
.withPluginClasspath()
.withArguments(listOf("--info", "-g", homeDir.absolutePath, "compileJava"))
.withArguments(listOf("--info", "-g", homeDir.absolutePath, "compileJava") + extraArguments)
.withProjectDir(projectDir)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package org.gradle.toolchains.foojay
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class FoojayToolchainsPluginFunctionalTest: AbstractFoojayToolchainsPluginFunctionalTest() {
class FoojayToolchainsPluginFunctionalTest : AbstractFoojayToolchainsPluginFunctionalTest() {

@ParameterizedTest(name = "gradle version: {0}")
@MethodSource("getGradleTestVersions")
Expand Down Expand Up @@ -43,6 +44,50 @@ class FoojayToolchainsPluginFunctionalTest: AbstractFoojayToolchainsPluginFuncti
assertProvisioningSuccessful(result)
}

@ParameterizedTest(name = "gradle version: {0}")
@MethodSource("getGradleTestVersions")
fun `can use base plugin with proxy`(gradleVersion: String) {
val settings = """
plugins {
id("org.gradle.toolchains.foojay-resolver")
}

toolchainManagement {
jvm {
javaRepositories {
repository("foojay") {
resolverClass.set(org.gradle.toolchains.foojay.FoojayToolchainResolver::class.java)
}
}
}
}
""".trimIndent()

val buildScript = """
plugins {
java
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(${getDifferentJavaVersion()}))
}
}
"""
val arguments = listOf(
"-Dhttps.proxyHost=127.0.0.1",
"-Dhttps.proxyPort=$proxyPort",
"-Dhttps.nonProxyHosts=''",
// To make sure Gradle does not pick up local JDK installations.
"-Porg.gradle.java.installations.auto-detect=false"
)
val result = runner(settings, buildScript, arguments)
.withGradleVersion(gradleVersion)
.build()
assertProvisioningSuccessful(result)
assertTrue(proxyInterceptorCount.get() > 0, "Traffic bypassed the proxy.")
}

@Test
fun `generates useful error for unsupported Gradle versions`() {
val settings = """
Expand Down Expand Up @@ -110,7 +155,9 @@ class FoojayToolchainsPluginFunctionalTest: AbstractFoojayToolchainsPluginFuncti
.withGradleVersion(gradleVersion)
.buildAndFail()

assertTrue("> Failed to apply plugin 'org.gradle.toolchains.foojay-resolver'.\n" +
" > Settings plugins must be applied in the settings script." in result.output)
assertTrue(
"> Failed to apply plugin 'org.gradle.toolchains.foojay-resolver'.\n" +
" > Settings plugins must be applied in the settings script." in result.output
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@ import org.gradle.jvm.toolchain.JvmImplementation
import org.gradle.jvm.toolchain.JvmVendorSpec
import org.gradle.platform.Architecture
import org.gradle.platform.OperatingSystem
import java.io.BufferedReader
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.net.URI
import java.net.URLEncoder
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.nio.charset.StandardCharsets.UTF_8
import java.util.concurrent.TimeUnit.SECONDS
import java.time.Duration

@Suppress("MagicNumber")
private val CONNECT_TIMEOUT = SECONDS.toMillis(10).toInt()

private val CONNECT_TIMEOUT = Duration.ofSeconds(10)
@Suppress("MagicNumber")
private val READ_TIMEOUT = SECONDS.toMillis(20).toInt()
private val READ_TIMEOUT = Duration.ofSeconds(20)

private const val SCHEMA = "https"

Expand All @@ -31,6 +30,10 @@ class FoojayApi {

private val distributions = mutableListOf<Distribution>()

private val httpClient = HttpClient.newBuilder()
.connectTimeout(CONNECT_TIMEOUT)
.build()

@Suppress("LongParameterList")
fun toPackage(
version: JavaLanguageVersion,
Expand All @@ -53,13 +56,10 @@ class FoojayApi {

private fun fetchDistributionsIfMissing() {
if (distributions.isEmpty()) {
val con = createConnection(
val json = downloadVendorList(
DISTRIBUTIONS_ENDPOINT,
mapOf("include_versions" to "true", "include_synonyms" to "true")
)
val json = readResponse(con)
con.disconnect()

distributions.addAll(parseDistributions(json))
}
}
Expand All @@ -70,8 +70,7 @@ class FoojayApi {
distributionName == "graalvm" -> "version"
else -> "jdk_version"
}

val con = createConnection(
val json = downloadVendorList(
PACKAGES_ENDPOINT,
mapOf(
versionApiKey to "$version",
Expand All @@ -81,36 +80,32 @@ class FoojayApi {
"directly_downloadable" to "true"
)
)
val json = readResponse(con)
con.disconnect()

val packages = parsePackages(json)
return match(packages, architecture)
}

private fun createConnection(endpoint: String, parameters: Map<String, String>): HttpURLConnection {
val url = URL("$SCHEMA://$endpoint?${toParameterString(parameters)}")
val con = url.openConnection() as HttpURLConnection
con.setRequestProperty("Content-Type", "application/json")
con.requestMethod = "GET"
con.connectTimeout = CONNECT_TIMEOUT
con.readTimeout = READ_TIMEOUT
return con
@Suppress("MagicNumber")
private fun downloadVendorList(
endpoint: String,
params: Map<String, String>
): String {
val uri = URI.create("$SCHEMA://$endpoint?${toParameterString(params)}")
val request = HttpRequest.newBuilder()
.uri(uri)
.header("Content-Type", "application/json")
.timeout(READ_TIMEOUT)
.GET()
.build()
val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() != 200) {
throw GradleException("Requesting vendor list failed: ${response.body()}")
}
return response.body()
}

private fun toParameterString(params: Map<String, String>): String {
return params.entries.joinToString("&") {
"${URLEncoder.encode(it.key, UTF_8.name())}=${URLEncoder.encode(it.value, UTF_8.name())}"
}
}

private fun readResponse(con: HttpURLConnection): String {
val status = con.responseCode
if (status != HttpURLConnection.HTTP_OK) {
throw GradleException("Requesting vendor list failed: ${readContent(con.errorStream)}")
}
return readContent(con.inputStream)
}

private fun readContent(stream: InputStream) = stream.bufferedReader().use(BufferedReader::readText)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import org.gradle.jvm.toolchain.JavaToolchainSpec
import org.gradle.util.GradleVersion
import java.util.*

@Suppress("UnstableApiUsage")
abstract class FoojayToolchainResolver : JavaToolchainResolver {

private val api: FoojayApi = FoojayApi()
private val api = FoojayApi()

override fun resolve(request: JavaToolchainRequest): Optional<JavaToolchainDownload> {
val spec = request.javaToolchainSpec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,20 @@
package org.gradle.toolchains.foojay

import org.gradle.api.initialization.Settings
import org.gradle.api.internal.SettingsInternal
import org.gradle.jvm.toolchain.JavaToolchainResolverRegistry
import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.util.GradleVersion

@Suppress("unused")
@Suppress("unused", "UnstableApiUsage")
abstract class FoojayToolchainsPlugin: AbstractFoojayToolchainPlugin() {

@Suppress("TooGenericExceptionThrown")
override fun apply(settings: Settings) {
if (GradleVersion.current().baseVersion < GradleVersion.version("7.6")) {
throw RuntimeException("${FoojayToolchainsPlugin::class.simpleName} needs Gradle version 7.6 or higher")
}

settings.plugins.apply("jvm-toolchain-management")

val registry = (settings as SettingsInternal).services.get(JavaToolchainResolverRegistry::class.java)
registry.register(FoojayToolchainResolver::class.java)
settings.serviceOf<JavaToolchainResolverRegistry>()
.apply { register(FoojayToolchainResolver::class.java) }
}

}
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[libraries]
gson = { module = "com.google.code.gson:gson", version = "2.14.0" }
littleproxy = { module = "io.github.littleproxy:littleproxy", version = "2.7.0" }

[plugins]
plugin-publish = { id = "com.gradle.plugin-publish", version = "2.1.1" }
Expand Down
Loading