This is an automated email from the ASF dual-hosted git repository.

vladimirsitnikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git


The following commit(s) were added to refs/heads/master by this push:
     new 5df1cd0017 chore: use 127.0.0.1 or ::1 for client-server tests
5df1cd0017 is described below

commit 5df1cd0017c80af2580fa93f46205dfc7f586425
Author: Vladimir Sitnikov <[email protected]>
AuthorDate: Sat Oct 25 20:57:10 2025 +0300

    chore: use 127.0.0.1 or ::1 for client-server tests
    
    InetAddress.getLocalHost().canonicalHostName might resolve to a public NAT
    DNS name, and there will be no way to bind a server port for it.
    
    Here's an example how the bind fails in Apache Jenkins EC2 instance:
    
    [server] Server failed to start: java.rmi.server.ExportException: Listen 
failed on port: 0; nested exception is:
    [server]        java.io.IOException: Could not bind to 
bb-worker1-ec2-va.apache.org/3.234.254.195 using port 0
    
    The workaround is to avoid InetAddress.getLocalHost() altogether,
    so we use 127.0.0.1 when IPv4 is available or ::1 otherwise.
---
 .../jmeter/buildtools/batchtest/BatchTest.kt       | 46 ++++++++++++++++++++--
 .../jmeter/buildtools/batchtest/BatchTestServer.kt | 28 ++++++-------
 2 files changed, 55 insertions(+), 19 deletions(-)

diff --git 
a/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTest.kt
 
b/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTest.kt
index cf19f44612..45291f1eb7 100644
--- 
a/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTest.kt
+++ 
b/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTest.kt
@@ -32,9 +32,12 @@ import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.JavaExec
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
 import org.gradle.kotlin.dsl.property
 import java.io.File
 import java.net.InetAddress
+import java.net.ServerSocket
 import javax.inject.Inject
 
 open class BatchTest @Inject constructor(objects: ObjectFactory) : JavaExec() {
@@ -107,13 +110,23 @@ open class BatchTest @Inject constructor(objects: 
ObjectFactory) : JavaExec() {
         get() = 
project.rootProject.layout.projectDirectory.dir("lib").asFileTree
 
     @get:InputFile
+    @get:PathSensitive(PathSensitivity.NONE)
     val jmeterJar
         get() = 
project.rootProject.layout.projectDirectory.dir("bin").file("ApacheJMeter.jar")
 
+    @Internal
+    val serverWorkingDir = objects.directoryProperty()
+        .convention(project.rootProject.layout.projectDirectory.dir("bin"))
+
+    @InputFile
+    @PathSensitive(PathSensitivity.NONE)
+    val jmeterProperties = objects.fileProperty()
+        .convention(serverWorkingDir.file("jmeter.properties"))
+
     init {
         group = BATCH_TESTS_GROUP_NAME
         description = "Runs jmx file via process fork and verifies outputs"
-        workingDir = File(project.rootDir, "bin")
+        workingDir = serverWorkingDir.get().asFile
         mainClass.set("org.apache.jmeter.NewDriver")
         classpath(jmeterJar)
 
@@ -121,10 +134,37 @@ open class BatchTest @Inject constructor(objects: 
ObjectFactory) : JavaExec() {
         // It enables to override the properties later (e.g. in the build 
script)
         maxHeapSize = "128m"
         jvmArgs("-Xss256k", "-XX:MaxMetaspaceSize=128m")
-        systemProperty("java.rmi.server.hostname", 
InetAddress.getLocalHost().canonicalHostName)
+        systemProperty("java.rmi.server.hostname", getServerHost())
         systemProperty("java.awt.headless", "true")
     }
 
+    @Internal
+    protected fun getServerHost(): String {
+        // If JVM has IPv4 enabled, 127.0.0.1 will work; otherwise fall back 
to ::1
+        return try {
+            // Will throw if IPv4 is completely disabled in the JVM/OS
+            InetAddress.getByAddress(byteArrayOf(127, 0, 0, 1))
+            "127.0.0.1"
+        } catch (_: Exception) {
+            "::1"
+        }
+    }
+
+    protected fun getFreePort(retries: Int = 10): Int {
+        repeat(retries - 1) {
+            try {
+                ServerSocket(0).use {
+                    return it.localPort
+                }
+            } catch (_: Exception) {
+                Thread.sleep(25)
+            }
+        }
+        ServerSocket(0).use {
+            return it.localPort
+        }
+    }
+
     fun jmeterArgument(name: String, value: String) {
         args("-J$name=$value")
     }
@@ -162,7 +202,7 @@ open class BatchTest @Inject constructor(objects: 
ObjectFactory) : JavaExec() {
         systemProperty("user.language", userLanguage.get())
         systemProperty("user.region", userRegion.get())
         systemProperty("user.country", userCountry.get())
-        args("-pjmeter.properties")
+        args("-p${jmeterProperties.get().asFile.name}")
         args("-q", batchProperties.get())
         args("-n")
         args("-t", jmx.get())
diff --git 
a/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTestServer.kt
 
b/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTestServer.kt
index b48369d62c..096867d4db 100644
--- 
a/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTestServer.kt
+++ 
b/build-logic/batchtest/src/main/kotlin/org/apache/jmeter/buildtools/batchtest/BatchTestServer.kt
@@ -25,11 +25,9 @@ import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.OutputFile
 import org.gradle.kotlin.dsl.findByType
 import org.gradle.kotlin.dsl.property
+import org.gradle.process.ExecOperations
 import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
-import java.io.File
 import java.net.ConnectException
-import java.net.InetAddress
-import java.net.ServerSocket
 import java.net.Socket
 import java.time.Duration
 import java.util.concurrent.Executors
@@ -37,7 +35,7 @@ import java.util.concurrent.TimeUnit
 import java.util.concurrent.TimeoutException
 import javax.inject.Inject
 
-open class BatchTestServer @Inject constructor(objects: ObjectFactory) : 
BatchTest(objects) {
+abstract class BatchTestServer @Inject constructor(objects: ObjectFactory) : 
BatchTest(objects) {
     private val executor =
         
Executors.newFixedThreadPool(project.gradle.startParameter.maxWorkerCount)
 
@@ -53,22 +51,20 @@ open class BatchTestServer @Inject constructor(objects: 
ObjectFactory) : BatchTe
     val startupTimeout = objects.property<Duration>()
         .convention(Duration.ofSeconds(15))
 
+    @get:Inject
+    abstract val execOperations: ExecOperations
+
     private fun deleteWorkfiles() {
         project.delete(serverLogFile)
     }
 
-    private fun getFreePort(): Int =
-        ServerSocket(0).use {
-            return it.localPort
-        }
-
     private fun waitForPort(host: String, port: Int, timeout: Duration): 
Boolean {
-        val deadline = System.nanoTime() + timeout.toNanos()
-        while (System.nanoTime() < deadline) {
+        val start = System.nanoTime()
+        while (System.nanoTime() - start <  timeout.toNanos()) {
             try {
                 Socket(host, port).close()
                 return true
-            } catch (e: ConnectException) {
+            } catch (_: ConnectException) {
                 /* ignore */
                 Thread.sleep(50)
             }
@@ -86,7 +82,7 @@ open class BatchTestServer @Inject constructor(objects: 
ObjectFactory) : BatchTe
 
         val client = this
         val serverPort = getFreePort()
-        val serverHost = InetAddress.getLocalHost().canonicalHostName
+        val serverHost = getServerHost()
         args("-R$serverHost:$serverPort")
         val jacoco = extensions.findByType<JacocoTaskExtension>()
         // The extension might not exist, so don't fail if so
@@ -106,8 +102,8 @@ open class BatchTestServer @Inject constructor(objects: 
ObjectFactory) : BatchTe
             jvmarg
         }
         val server = executor.submit {
-            project.javaexec {
-                workingDir = File(project.rootDir, "bin")
+            execOperations.javaexec {
+                workingDir = serverWorkingDir.get().asFile
                 mainClass.set("org.apache.jmeter.NewDriver")
                 classpath(client.classpath)
                 standardOutput = System.out.writer().withPrefix("[server] ")
@@ -124,7 +120,7 @@ open class BatchTestServer @Inject constructor(objects: 
ObjectFactory) : BatchTe
                 systemProperty("user.region", userRegion.get())
                 systemProperty("user.country", userCountry.get())
 
-                args("-pjmeter.properties")
+                args("-p${jmeterProperties.get().asFile.name}")
                 args("-q", batchProperties.get())
                 args("-i", log4jXml.get())
                 args("-j", serverLogFile.get())

Reply via email to