From 3e92b5c3d456fe2de488c7ef97a9114eceee6201 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Wed, 11 Feb 2026 12:17:47 -0500 Subject: [PATCH 01/16] GEODE-10556: Fix race condition in PulseSecurityWithSSLTest Increase wait time to 10 seconds after verifying ManagementService and Pulse web app readiness, to allow SSL/JMX authentication backend full initialization. The key issue is that this test starts the locator within each test method, unlike PulseSecurityIntegrationTest which uses withAutoStart() causing the locator to start before any tests run. SSL handshakes and JMX registration under security manager require additional time on slower CI runners. Added debug logging to diagnose timing. 10 seconds is conservative to ensure reliability on slow CI; can be reduced once we confirm this resolves the issue. --- .../tools/pulse/PulseSecurityWithSSLTest.java | 85 ++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java index efcd704b744..e4c4b20f2bb 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java @@ -36,6 +36,7 @@ import static org.apache.geode.distributed.ConfigurationProperties.SSL_PROTOCOLS; import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE; import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD; +import static org.apache.geode.test.awaitility.GeodeAwaitility.await; import static org.apache.geode.test.util.ResourceUtils.createTempFileFromResource; import static org.assertj.core.api.Assertions.assertThat; @@ -51,6 +52,7 @@ import org.junit.experimental.categories.Category; import org.apache.geode.examples.SimpleSecurityManager; +import org.apache.geode.management.ManagementService; import org.apache.geode.security.SecurableCommunicationChannels; import org.apache.geode.test.junit.categories.PulseTest; import org.apache.geode.test.junit.categories.SecurityTest; @@ -83,13 +85,54 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { locator.withSecurityManager(SimpleSecurityManager.class).withProperties(securityProps) .startLocator(); - + System.out.println("[DEBUG] Locator started on port: " + locator.getHttpPort()); + + // Wait for JMX/Management Service to be fully initialized before login attempts + // This prevents race conditions on slower CI machines where authentication may fail + // if the JMX backend is not ready yet + ManagementService service = + ManagementService.getExistingManagementService(locator.getLocator().getCache()); + System.out.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); + await() + .untilAsserted(() -> assertThat(service.getMemberMXBean()).isNotNull()); + System.out.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); + + // Additionally wait for Pulse web application to be fully responsive + System.out.println("[DEBUG] Waiting for Pulse web app to respond..."); + await() + .untilAsserted(() -> { + ClassicHttpResponse loginPageResponse = client.get("/pulse/login.html"); + try { + assertThat(loginPageResponse.getCode()).isEqualTo(200); + } finally { + // Ensure entity is consumed to return connection to pool + if (loginPageResponse.getEntity() != null) { + try { + loginPageResponse.getEntity().getContent().close(); + } catch (Exception ignore) { + } + } + } + }); + System.out.println("[DEBUG] Pulse web app is responsive"); + + System.out.println("[DEBUG] Attempting login with wrong password..."); ClassicHttpResponse response = client.loginToPulse("data", "wrongPassword"); + System.out.println("[DEBUG] Wrong password response: " + response.getCode() + ", Location: " + + (response.getFirstHeader("Location") != null + ? response.getFirstHeader("Location").getValue() : "null")); assertThat(response.getCode()).isEqualTo(302); assertThat(response.getFirstHeader("Location").getValue()) .contains("/pulse/login.html?error=BAD_CREDS"); + // SSL + JMX authentication backend needs significant time to initialize on CI + // PulseSecurityIntegrationTest works because it uses withAutoStart() giving more init time + System.out.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); + Thread.sleep(10000); + + System.out.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); client.loginToPulseAndVerify("cluster", "cluster"); + System.out.println("[DEBUG] Login successful!"); // Ensure that the backend JMX connection is working too response = client.post("/pulse/pulseUpdate", "pulseData", @@ -122,8 +165,46 @@ public void loginWithDeprecatedSSLOptions() throws Exception { locator.withSecurityManager(SimpleSecurityManager.class).withProperties(securityProps) .startLocator(); - + System.out + .println("[DEBUG] Locator started (deprecated SSL) on port: " + locator.getHttpPort()); + + // Wait for JMX/Management Service to be fully initialized before login attempts + // This prevents race conditions on slower CI machines where authentication may fail + // if the JMX backend is not ready yet + ManagementService service = + ManagementService.getExistingManagementService(locator.getLocator().getCache()); + System.out.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); + await() + .untilAsserted(() -> assertThat(service.getMemberMXBean()).isNotNull()); + System.out.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); + + // Additionally wait for Pulse web application to be fully responsive + System.out.println("[DEBUG] Waiting for Pulse web app to respond..."); + await() + .untilAsserted(() -> { + ClassicHttpResponse loginPageResponse = client.get("/pulse/login.html"); + try { + assertThat(loginPageResponse.getCode()).isEqualTo(200); + } finally { + // Ensure entity is consumed to return connection to pool + if (loginPageResponse.getEntity() != null) { + try { + loginPageResponse.getEntity().getContent().close(); + } catch (Exception ignore) { + } + } + } + }); + System.out.println("[DEBUG] Pulse web app is responsive"); + + // SSL + JMX authentication backend needs significant time to initialize on CI + // PulseSecurityIntegrationTest works because it uses withAutoStart() giving more init time + System.out.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); + Thread.sleep(10000); + + System.out.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); client.loginToPulseAndVerify("cluster", "cluster"); + System.out.println("[DEBUG] Login successful!"); // Ensure that the backend JMX connection is working too ClassicHttpResponse response = client.post("/pulse/pulseUpdate", "pulseData", From e3392145287dbef312a68a942ff4feda45bdf200 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Wed, 11 Feb 2026 20:56:58 -0500 Subject: [PATCH 02/16] Add debug logging to diagnose SSL authentication timing issues - Replace System.out with System.err for better capture in test reports - Add @Before method to log test start with environment details - Debug statements will appear in HTML reports and CI artifacts when tests fail --- .../tools/pulse/PulseSecurityWithSSLTest.java | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java index e4c4b20f2bb..1c93462019d 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java @@ -47,6 +47,7 @@ import com.jayway.jsonpath.JsonPath; import org.apache.commons.io.IOUtils; import org.apache.hc.core5.http.ClassicHttpResponse; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -72,6 +73,17 @@ public class PulseSecurityWithSSLTest { @Rule public GeodeHttpClientRule client = new GeodeHttpClientRule(locator::getHttpPort).withSSL(); + @Before + public void logTestStart() { + String testName = Thread.currentThread().getStackTrace()[2].getMethodName(); + System.err.println("========================================"); + System.err.println("[TEST START] " + getClass().getSimpleName() + "." + testName); + System.err.println("[TEST] Java Version: " + System.getProperty("java.version")); + System.err.println("[TEST] OS: " + System.getProperty("os.name")); + System.err.println("[TEST] Timestamp: " + java.time.Instant.now()); + System.err.println("========================================"); + } + @Test public void loginWithIncorrectAndThenCorrectPassword() throws Exception { Properties securityProps = new Properties(); @@ -85,20 +97,20 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { locator.withSecurityManager(SimpleSecurityManager.class).withProperties(securityProps) .startLocator(); - System.out.println("[DEBUG] Locator started on port: " + locator.getHttpPort()); + System.err.println("[DEBUG] Locator started on port: " + locator.getHttpPort()); // Wait for JMX/Management Service to be fully initialized before login attempts // This prevents race conditions on slower CI machines where authentication may fail // if the JMX backend is not ready yet ManagementService service = ManagementService.getExistingManagementService(locator.getLocator().getCache()); - System.out.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); + System.err.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); await() .untilAsserted(() -> assertThat(service.getMemberMXBean()).isNotNull()); - System.out.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); + System.err.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); // Additionally wait for Pulse web application to be fully responsive - System.out.println("[DEBUG] Waiting for Pulse web app to respond..."); + System.err.println("[DEBUG] Waiting for Pulse web app to respond..."); await() .untilAsserted(() -> { ClassicHttpResponse loginPageResponse = client.get("/pulse/login.html"); @@ -114,11 +126,11 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { } } }); - System.out.println("[DEBUG] Pulse web app is responsive"); + System.err.println("[DEBUG] Pulse web app is responsive"); - System.out.println("[DEBUG] Attempting login with wrong password..."); + System.err.println("[DEBUG] Attempting login with wrong password..."); ClassicHttpResponse response = client.loginToPulse("data", "wrongPassword"); - System.out.println("[DEBUG] Wrong password response: " + response.getCode() + ", Location: " + System.err.println("[DEBUG] Wrong password response: " + response.getCode() + ", Location: " + (response.getFirstHeader("Location") != null ? response.getFirstHeader("Location").getValue() : "null")); assertThat(response.getCode()).isEqualTo(302); @@ -127,12 +139,12 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { // SSL + JMX authentication backend needs significant time to initialize on CI // PulseSecurityIntegrationTest works because it uses withAutoStart() giving more init time - System.out.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); + System.err.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); Thread.sleep(10000); - System.out.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); + System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); client.loginToPulseAndVerify("cluster", "cluster"); - System.out.println("[DEBUG] Login successful!"); + System.err.println("[DEBUG] Login successful!"); // Ensure that the backend JMX connection is working too response = client.post("/pulse/pulseUpdate", "pulseData", @@ -165,7 +177,7 @@ public void loginWithDeprecatedSSLOptions() throws Exception { locator.withSecurityManager(SimpleSecurityManager.class).withProperties(securityProps) .startLocator(); - System.out + System.err .println("[DEBUG] Locator started (deprecated SSL) on port: " + locator.getHttpPort()); // Wait for JMX/Management Service to be fully initialized before login attempts @@ -173,13 +185,13 @@ public void loginWithDeprecatedSSLOptions() throws Exception { // if the JMX backend is not ready yet ManagementService service = ManagementService.getExistingManagementService(locator.getLocator().getCache()); - System.out.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); + System.err.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); await() .untilAsserted(() -> assertThat(service.getMemberMXBean()).isNotNull()); - System.out.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); + System.err.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); // Additionally wait for Pulse web application to be fully responsive - System.out.println("[DEBUG] Waiting for Pulse web app to respond..."); + System.err.println("[DEBUG] Waiting for Pulse web app to respond..."); await() .untilAsserted(() -> { ClassicHttpResponse loginPageResponse = client.get("/pulse/login.html"); @@ -195,16 +207,16 @@ public void loginWithDeprecatedSSLOptions() throws Exception { } } }); - System.out.println("[DEBUG] Pulse web app is responsive"); + System.err.println("[DEBUG] Pulse web app is responsive"); // SSL + JMX authentication backend needs significant time to initialize on CI // PulseSecurityIntegrationTest works because it uses withAutoStart() giving more init time - System.out.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); + System.err.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); Thread.sleep(10000); - System.out.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); + System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); client.loginToPulseAndVerify("cluster", "cluster"); - System.out.println("[DEBUG] Login successful!"); + System.err.println("[DEBUG] Login successful!"); // Ensure that the backend JMX connection is working too ClassicHttpResponse response = client.post("/pulse/pulseUpdate", "pulseData", From 3103e8eedd6db8a9f851dc5dd2fba122057f4048 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 05:43:05 -0500 Subject: [PATCH 03/16] Fix PulseSecurityWithSSLTest hanging on CI by adding HTTP timeouts and retry logic Root cause: HTTP client had no timeouts, so when Pulse JMX authentication backend wasn't ready, the POST /pulse/login hung indefinitely instead of timing out. Debug output showed: - Wrong password login worked immediately (fast auth check) - Correct password login hung forever (JMX connection establishment not ready) Changes: 1. Add 30-second connection and response timeouts to GeodeHttpClientRule 2. Replace fixed 10-second sleep with await() polling that retries login until backend is ready 3. Login attempts will now timeout after 30s and retry (up to 5 minutes total) This allows the test to handle varying JMX backend initialization times on CI. --- .../test/junit/rules/GeodeHttpClientRule.java | 13 +++++++ .../tools/pulse/PulseSecurityWithSSLTest.java | 34 ++++++++++--------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index b6dec6eb47a..c29f7a15eaf 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -25,6 +25,7 @@ import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; @@ -185,6 +186,14 @@ public boolean isRedirected( }; host = new HttpHost(useSSL ? "https" : "http", hostName, portSupplier.get()); + + // Configure timeouts to prevent tests from hanging indefinitely + // when Pulse JMX authentication backend is not ready + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) + .setResponseTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) + .build(); + if (useSSL) { HttpClientBuilder clientBuilder = HttpClients.custom(); SSLContext ctx = SocketCreatorFactory @@ -200,19 +209,23 @@ public boolean isRedirected( clientBuilder.setConnectionManager(connectionManager); // Jakarta EE migration: Create client with lenient redirect strategy clientBuilder.setRedirectStrategy(lenientRedirectStrategy); + clientBuilder.setDefaultRequestConfig(requestConfig); httpClient = clientBuilder.build(); // Jakarta EE migration: Create client without redirect handling for login verification httpClientNoRedirect = HttpClients.custom() .setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig) .disableRedirectHandling() .build(); } else { // Jakarta EE migration: Create client with lenient redirect strategy httpClient = HttpClients.custom() .setRedirectStrategy(lenientRedirectStrategy) + .setDefaultRequestConfig(requestConfig) .build(); // Jakarta EE migration: Create client without redirect handling for login verification httpClientNoRedirect = HttpClients.custom() + .setDefaultRequestConfig(requestConfig) .disableRedirectHandling() .build(); } diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java index 1c93462019d..18c245c0969 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java @@ -137,14 +137,15 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { assertThat(response.getFirstHeader("Location").getValue()) .contains("/pulse/login.html?error=BAD_CREDS"); - // SSL + JMX authentication backend needs significant time to initialize on CI - // PulseSecurityIntegrationTest works because it uses withAutoStart() giving more init time - System.err.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); - Thread.sleep(10000); - - System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); - client.loginToPulseAndVerify("cluster", "cluster"); - System.err.println("[DEBUG] Login successful!"); + // Wait for JMX authentication backend to be ready by polling login attempts + // The backend may timeout initially while JMX connections are being established + System.err.println("[DEBUG] Waiting for JMX authentication backend to be ready..."); + await() + .untilAsserted(() -> { + System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); + client.loginToPulseAndVerify("cluster", "cluster"); + System.err.println("[DEBUG] Login successful!"); + }); // Ensure that the backend JMX connection is working too response = client.post("/pulse/pulseUpdate", "pulseData", @@ -209,14 +210,15 @@ public void loginWithDeprecatedSSLOptions() throws Exception { }); System.err.println("[DEBUG] Pulse web app is responsive"); - // SSL + JMX authentication backend needs significant time to initialize on CI - // PulseSecurityIntegrationTest works because it uses withAutoStart() giving more init time - System.err.println("[DEBUG] Waiting 10 seconds for SSL/JMX authentication backend..."); - Thread.sleep(10000); - - System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); - client.loginToPulseAndVerify("cluster", "cluster"); - System.err.println("[DEBUG] Login successful!"); + // Wait for JMX authentication backend to be ready by polling login attempts + // The backend may timeout initially while JMX connections are being established + System.err.println("[DEBUG] Waiting for JMX authentication backend to be ready..."); + await() + .untilAsserted(() -> { + System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); + client.loginToPulseAndVerify("cluster", "cluster"); + System.err.println("[DEBUG] Login successful!"); + }); // Ensure that the backend JMX connection is working too ClassicHttpResponse response = client.post("/pulse/pulseUpdate", "pulseData", From 085565e7cb8ac0f6a88f659cc6cc53ffc98b879d Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 05:53:15 -0500 Subject: [PATCH 04/16] Apply spotless formatting --- .../apache/geode/test/junit/rules/GeodeHttpClientRule.java | 4 ++-- .../apache/geode/tools/pulse/PulseSecurityWithSSLTest.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index c29f7a15eaf..d44ca5cd3b2 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -186,14 +186,14 @@ public boolean isRedirected( }; host = new HttpHost(useSSL ? "https" : "http", hostName, portSupplier.get()); - + // Configure timeouts to prevent tests from hanging indefinitely // when Pulse JMX authentication backend is not ready RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) .setResponseTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) .build(); - + if (useSSL) { HttpClientBuilder clientBuilder = HttpClients.custom(); SSLContext ctx = SocketCreatorFactory diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java index 18c245c0969..da23167bebd 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java @@ -142,7 +142,8 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { System.err.println("[DEBUG] Waiting for JMX authentication backend to be ready..."); await() .untilAsserted(() -> { - System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); + System.err + .println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); client.loginToPulseAndVerify("cluster", "cluster"); System.err.println("[DEBUG] Login successful!"); }); @@ -215,7 +216,8 @@ public void loginWithDeprecatedSSLOptions() throws Exception { System.err.println("[DEBUG] Waiting for JMX authentication backend to be ready..."); await() .untilAsserted(() -> { - System.err.println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); + System.err + .println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); client.loginToPulseAndVerify("cluster", "cluster"); System.err.println("[DEBUG] Login successful!"); }); From 513dbeaa53973bee355bf7bbb7bf202d3c5d2f3f Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 08:15:56 -0500 Subject: [PATCH 05/16] Fix HTTP timeout configuration: use setConnectTimeout instead of setConnectionRequestTimeout The ConnectionRequestTimeout is for acquiring connections from the pool, but the actual issue is establishing TCP/SSL connections when the server is slow to start. Changed to setConnectTimeout to properly timeout connection establishment attempts during test execution. --- .../org/apache/geode/test/junit/rules/GeodeHttpClientRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index d44ca5cd3b2..ab9798c73f3 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -190,7 +190,7 @@ public boolean isRedirected( // Configure timeouts to prevent tests from hanging indefinitely // when Pulse JMX authentication backend is not ready RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) + .setConnectTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) .setResponseTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) .build(); From 4c582bfeb82dd6bd4eb462f5d64982a27bdc7a00 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 09:25:35 -0500 Subject: [PATCH 06/16] Add setConnectionRequestTimeout back - all three timeout types needed HttpClient 5.x requires all three timeout configurations: 1. setConnectionRequestTimeout - timeout for acquiring connection from pool 2. setConnectTimeout - timeout for establishing TCP/SSL connection 3. setResponseTimeout - timeout for receiving response data The CI was still getting ConnectionRequestTimeoutException because we only had setConnectTimeout. When the SSL connection hangs, the pool waits indefinitely to reclaim/allocate the connection. All three timeouts work together to prevent hangs at different stages. --- .../org/apache/geode/test/junit/rules/GeodeHttpClientRule.java | 1 + 1 file changed, 1 insertion(+) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index ab9798c73f3..b811ddd3633 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -190,6 +190,7 @@ public boolean isRedirected( // Configure timeouts to prevent tests from hanging indefinitely // when Pulse JMX authentication backend is not ready RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) .setConnectTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) .setResponseTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) .build(); From 525ad71d311742541cf8a1b6047da9a2a825652c Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 10:38:56 -0500 Subject: [PATCH 07/16] Reduce HTTP timeouts to 5s and add retry logging for CI debugging Changes: 1. GeodeHttpClientRule: Reduce HTTP timeouts from 30s to 5s - Allows await() retry loop to poll every 5s instead of 30s - Server gets ~6x more retry attempts within same timeframe - Tests pass locally with this configuration 2. PulseSecurityWithSSLTest: Add exception logging in retry blocks - Shows retry attempts and failure reasons on CI - Helps diagnose JMX backend initialization timing issues - Logs exception type and message before retrying 3. geode-lucene: Increase heap size from 12g to 16g - Prevents OutOfMemoryError during classpath scanning - Unrelated to Pulse changes but hit during CI test runs Root cause: Pulse JMX authentication backend takes longer to initialize on CI than locally. The 5s timeout + await() retry strategy allows tests to poll frequently until backend is ready, rather than hanging for 30s per failed attempt. --- .../test/junit/rules/GeodeHttpClientRule.java | 10 ++++++---- .../tools/pulse/PulseSecurityWithSSLTest.java | 20 +++++++++++++++---- geode-lucene/build.gradle | 2 +- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index b811ddd3633..66b04629e4e 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -188,11 +188,13 @@ public boolean isRedirected( host = new HttpHost(useSSL ? "https" : "http", hostName, portSupplier.get()); // Configure timeouts to prevent tests from hanging indefinitely - // when Pulse JMX authentication backend is not ready + // when Pulse JMX authentication backend is not ready. + // Using shorter timeouts (5s) allows await() retry logic to poll more frequently + // rather than waiting 30s per failed attempt. RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) - .setConnectTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) - .setResponseTimeout(30000, java.util.concurrent.TimeUnit.MILLISECONDS) + .setConnectionRequestTimeout(5000, java.util.concurrent.TimeUnit.MILLISECONDS) + .setConnectTimeout(5000, java.util.concurrent.TimeUnit.MILLISECONDS) + .setResponseTimeout(5000, java.util.concurrent.TimeUnit.MILLISECONDS) .build(); if (useSSL) { diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java index da23167bebd..109820f6481 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java @@ -144,8 +144,14 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { .untilAsserted(() -> { System.err .println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); - client.loginToPulseAndVerify("cluster", "cluster"); - System.err.println("[DEBUG] Login successful!"); + try { + client.loginToPulseAndVerify("cluster", "cluster"); + System.err.println("[DEBUG] Login successful!"); + } catch (Exception e) { + System.err.println("[DEBUG] Login failed: " + e.getClass().getSimpleName() + ": " + + e.getMessage() + " - will retry"); + throw e; // Re-throw so await() can catch and retry + } }); // Ensure that the backend JMX connection is working too @@ -218,8 +224,14 @@ public void loginWithDeprecatedSSLOptions() throws Exception { .untilAsserted(() -> { System.err .println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); - client.loginToPulseAndVerify("cluster", "cluster"); - System.err.println("[DEBUG] Login successful!"); + try { + client.loginToPulseAndVerify("cluster", "cluster"); + System.err.println("[DEBUG] Login successful!"); + } catch (Exception e) { + System.err.println("[DEBUG] Login failed: " + e.getClass().getSimpleName() + ": " + + e.getMessage() + " - will retry"); + throw e; // Re-throw so await() can catch and retry + } }); // Ensure that the backend JMX connection is working too diff --git a/geode-lucene/build.gradle b/geode-lucene/build.gradle index 44662d20056..05c87b69810 100644 --- a/geode-lucene/build.gradle +++ b/geode-lucene/build.gradle @@ -87,6 +87,6 @@ integrationTest.forkEvery 5 // Increase heap size for Lucene integration tests to prevent OutOfMemoryError // Jakarta migration introduced ByteBuffersDirectory which may have different memory characteristics integrationTest { - maxHeapSize = '12g' + maxHeapSize = '16g' jvmArgs '-XX:+UseG1GC', '-XX:MaxGCPauseMillis=500', '-XX:+HeapDumpOnOutOfMemoryError' } From 8e879878d23f40eea7597d200bb36f8ffca5b54a Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 11:46:42 -0500 Subject: [PATCH 08/16] Fix await() retry logic: wrap exceptions in AssertionError Critical fix: await().untilAsserted() ONLY catches AssertionError, not general exceptions. The ConnectionRequestTimeoutException was escaping the retry loop because we were re-throwing it as-is. Changed: throw e; // Wrong - escapes retry loop To: throw new AssertionError("Login failed, will retry", e); // Correct Now the 5-second HTTP timeouts will properly trigger retries instead of failing the test immediately. The await() loop can now poll every 5s until the JMX backend is ready (up to 5-minute timeout). Tests pass locally with this fix. --- .../apache/geode/tools/pulse/PulseSecurityWithSSLTest.java | 4 ++-- geode-lucene/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java index 109820f6481..b0dd2fe86bf 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java @@ -150,7 +150,7 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { } catch (Exception e) { System.err.println("[DEBUG] Login failed: " + e.getClass().getSimpleName() + ": " + e.getMessage() + " - will retry"); - throw e; // Re-throw so await() can catch and retry + throw new AssertionError("Login failed, will retry", e); } }); @@ -230,7 +230,7 @@ public void loginWithDeprecatedSSLOptions() throws Exception { } catch (Exception e) { System.err.println("[DEBUG] Login failed: " + e.getClass().getSimpleName() + ": " + e.getMessage() + " - will retry"); - throw e; // Re-throw so await() can catch and retry + throw new AssertionError("Login failed, will retry", e); } }); diff --git a/geode-lucene/build.gradle b/geode-lucene/build.gradle index 05c87b69810..44662d20056 100644 --- a/geode-lucene/build.gradle +++ b/geode-lucene/build.gradle @@ -87,6 +87,6 @@ integrationTest.forkEvery 5 // Increase heap size for Lucene integration tests to prevent OutOfMemoryError // Jakarta migration introduced ByteBuffersDirectory which may have different memory characteristics integrationTest { - maxHeapSize = '16g' + maxHeapSize = '12g' jvmArgs '-XX:+UseG1GC', '-XX:MaxGCPauseMillis=500', '-XX:+HeapDumpOnOutOfMemoryError' } From 34ac080db03620b22f673e701813c7b74ed10387 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 14:01:15 -0500 Subject: [PATCH 09/16] Increase HTTP connection pool size to prevent exhaustion during retries Problem: Tests timeout after 5 minutes on CI because the connection pool exhausts after a few failed login attempts. Default pool settings: - 2 connections per route (too small for retry loops) - 20 max total connections When loginToPulse() times out, the connection may not be properly returned to the pool, especially during TCP/SSL handshake failures. After a few timeouts, no connections are available and await() retry loop deadlocks. Solution: Increase connection pool configuration: - setMaxConnPerRoute(10) - up from default 2 - setMaxConnTotal(20) - keep at 20 but now with more per-route This allows multiple retry attempts without exhausting the pool while waiting for JMX backend initialization on slower CI environments. Tests pass locally with this configuration. --- .../geode/test/junit/rules/GeodeHttpClientRule.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index 66b04629e4e..1c0f56cee28 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -202,8 +202,11 @@ public boolean isRedirected( SSLContext ctx = SocketCreatorFactory .getSocketCreatorForComponent(SecurableCommunicationChannel.WEB).getSslContext(); // HttpClient 5.x: SSL configuration via connection manager + // Increase pool size and enable eviction to prevent connection exhaustion during retries HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() + .setMaxConnPerRoute(10) // Increase from default 2 to handle multiple retries + .setMaxConnTotal(20) // Increase from default 20 .setSSLSocketFactory(SSLConnectionSocketFactoryBuilder.create() .setSslContext(ctx) .setHostnameVerifier(NoopHostnameVerifier.INSTANCE) @@ -221,13 +224,21 @@ public boolean isRedirected( .disableRedirectHandling() .build(); } else { + // Configure connection pool for non-SSL client + HttpClientConnectionManager connectionManager = + PoolingHttpClientConnectionManagerBuilder.create() + .setMaxConnPerRoute(10) + .setMaxConnTotal(20) + .build(); // Jakarta EE migration: Create client with lenient redirect strategy httpClient = HttpClients.custom() + .setConnectionManager(connectionManager) .setRedirectStrategy(lenientRedirectStrategy) .setDefaultRequestConfig(requestConfig) .build(); // Jakarta EE migration: Create client without redirect handling for login verification httpClientNoRedirect = HttpClients.custom() + .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .disableRedirectHandling() .build(); From 0d5a1339717ad628a53f64b5fa97cf2dde015588 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 14:23:20 -0500 Subject: [PATCH 10/16] Apply formatting to connection pool comments --- .../apache/geode/test/junit/rules/GeodeHttpClientRule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index 1c0f56cee28..eaf5de8e4ae 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -205,8 +205,8 @@ public boolean isRedirected( // Increase pool size and enable eviction to prevent connection exhaustion during retries HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() - .setMaxConnPerRoute(10) // Increase from default 2 to handle multiple retries - .setMaxConnTotal(20) // Increase from default 20 + .setMaxConnPerRoute(10) // Increase from default 2 to handle multiple retries + .setMaxConnTotal(20) // Increase from default 20 .setSSLSocketFactory(SSLConnectionSocketFactoryBuilder.create() .setSslContext(ctx) .setHostnameVerifier(NoopHostnameVerifier.INSTANCE) From 805c746c04a08adfc6b7707f246b148099adbb47 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Thu, 12 Feb 2026 19:50:43 -0500 Subject: [PATCH 11/16] Enable automatic connection eviction in HttpClient Fixes connection pool exhaustion in PulseSecurityWithSSLTest CI failures. CI logs showed connections that timed out during SSL handshake or response were not returning to the pool. This caused exhaustion after exactly 10 attempts (the configured pool size), preventing the retry loop from continuing. Added: - evictExpiredConnections(): Automatically removes expired connections - evictIdleConnections(10s): Cleans up connections idle for 10+ seconds This allows the retry loop to continue beyond the initial pool size by reclaiming stale connections from failed authentication attempts. --- .../apache/geode/test/junit/rules/GeodeHttpClientRule.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index eaf5de8e4ae..c2d3fc51fbd 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -42,6 +42,7 @@ import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.net.URIBuilder; +import org.apache.hc.core5.util.TimeValue; import org.junit.rules.ExternalResource; import org.apache.geode.internal.net.SocketCreatorFactory; @@ -213,6 +214,9 @@ public boolean isRedirected( .build()) .build(); clientBuilder.setConnectionManager(connectionManager); + // Enable connection eviction to release stale connections + clientBuilder.evictExpiredConnections(); + clientBuilder.evictIdleConnections(TimeValue.ofSeconds(10)); // Jakarta EE migration: Create client with lenient redirect strategy clientBuilder.setRedirectStrategy(lenientRedirectStrategy); clientBuilder.setDefaultRequestConfig(requestConfig); @@ -235,6 +239,8 @@ public boolean isRedirected( .setConnectionManager(connectionManager) .setRedirectStrategy(lenientRedirectStrategy) .setDefaultRequestConfig(requestConfig) + .evictExpiredConnections() + .evictIdleConnections(TimeValue.ofSeconds(10)) .build(); // Jakarta EE migration: Create client without redirect handling for login verification httpClientNoRedirect = HttpClients.custom() From 0d531cf5424927751429fc3f1f133f000631d383 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Fri, 13 Feb 2026 05:58:21 -0500 Subject: [PATCH 12/16] Add comprehensive debug logging to identify CI timeout root cause --- .../test/junit/rules/GeodeHttpClientRule.java | 82 ++++++++++- .../tools/pulse/PulseSecurityWithSSLTest.java | 129 +++++++++++++++--- 2 files changed, 191 insertions(+), 20 deletions(-) diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java index c2d3fc51fbd..72824a55089 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/GeodeHttpClientRule.java @@ -100,9 +100,56 @@ public ClassicHttpResponse loginToPulse(String username, String password) throws HttpClientContext freshLoginContext = HttpClientContext.create(); // Explicitly set an empty cookie store to ensure no cookies from previous requests freshLoginContext.setCookieStore(new BasicCookieStore()); - ClassicHttpResponse response = (ClassicHttpResponse) httpClientNoRedirect.execute(host, - buildHttpPost("/pulse/login", "username", username, "password", password), - freshLoginContext); + + long startTime = System.currentTimeMillis(); + System.err.println("[HTTP] ===== HTTP POST REQUEST START ====="); + System.err + .println("[HTTP] Timestamp: " + java.time.Instant.now() + " (epoch: " + startTime + ")"); + System.err.println("[HTTP] Target: " + host + "/pulse/login"); + System.err.println("[HTTP] Username: " + username); + System.err.println("[HTTP] About to call httpClientNoRedirect.execute()..."); + + ClassicHttpResponse response; + try { + System.err.println("[HTTP] Executing HTTP request NOW at " + java.time.Instant.now()); + response = (ClassicHttpResponse) httpClientNoRedirect.execute(host, + buildHttpPost("/pulse/login", "username", username, "password", password), + freshLoginContext); + long endTime = System.currentTimeMillis(); + System.err.println("[HTTP] ===== HTTP POST REQUEST COMPLETED ====="); + System.err.println("[HTTP] Duration: " + (endTime - startTime) + "ms"); + System.err.println("[HTTP] Response code: " + response.getCode()); + System.err.println("[HTTP] Response Location header: " + + (response.getFirstHeader("Location") != null + ? response.getFirstHeader("Location").getValue() : "null")); + System.err.println("[HTTP] Completed at: " + java.time.Instant.now()); + } catch (org.apache.hc.client5.http.impl.classic.RequestFailedException e) { + long endTime = System.currentTimeMillis(); + System.err.println("[HTTP] RequestFailedException after " + (endTime - startTime) + + "ms: " + e.getMessage() + " (request aborted/cancelled)"); + throw e; + } catch (org.apache.hc.core5.http.ConnectionRequestTimeoutException e) { + long endTime = System.currentTimeMillis(); + System.err.println("[HTTP] ConnectionRequestTimeoutException after " + (endTime - startTime) + + "ms: " + e.getMessage() + " (pool exhausted waiting for connection)"); + throw e; + } catch (java.net.SocketTimeoutException e) { + long endTime = System.currentTimeMillis(); + System.err.println("[HTTP] SocketTimeoutException after " + (endTime - startTime) + + "ms: " + e.getMessage() + " (connect or read timeout)"); + throw e; + } catch (org.apache.hc.core5.http.ConnectionClosedException e) { + long endTime = System.currentTimeMillis(); + System.err.println("[HTTP] ConnectionClosedException after " + (endTime - startTime) + + "ms: " + e.getMessage() + " (connection closed by server)"); + throw e; + } catch (Exception e) { + long endTime = System.currentTimeMillis(); + System.err + .println("[HTTP] " + e.getClass().getSimpleName() + " after " + (endTime - startTime) + + "ms: " + e.getMessage()); + throw e; + } // If login is successful (302 redirect to clusterDetail), update shared context with new // session if (response.getCode() == 302 && response.getFirstHeader("Location") != null @@ -162,6 +209,10 @@ private void connect() { return; } + System.err + .println("[HTTP-INIT] Starting HTTP client initialization at " + java.time.Instant.now()); + System.err.println("[HTTP-INIT] SSL enabled: " + useSSL); + // Jakarta EE migration: Create lenient redirect strategy for URIs with placeholders // Apache HttpComponents 5 is stricter about URI validation than version 4 // This allows Spring Security OAuth2 redirect URIs with property placeholders @@ -186,7 +237,10 @@ public boolean isRedirected( } }; - host = new HttpHost(useSSL ? "https" : "http", hostName, portSupplier.get()); + int port = portSupplier.get(); + host = new HttpHost(useSSL ? "https" : "http", hostName, port); + System.err.println( + "[HTTP-INIT] Target host: " + (useSSL ? "https" : "http") + "://" + hostName + ":" + port); // Configure timeouts to prevent tests from hanging indefinitely // when Pulse JMX authentication backend is not ready. @@ -197,8 +251,11 @@ public boolean isRedirected( .setConnectTimeout(5000, java.util.concurrent.TimeUnit.MILLISECONDS) .setResponseTimeout(5000, java.util.concurrent.TimeUnit.MILLISECONDS) .build(); + System.err.println( + "[HTTP-INIT] Request config: ConnectionRequestTimeout=5s, ConnectTimeout=5s, ResponseTimeout=5s"); if (useSSL) { + System.err.println("[HTTP-INIT] Configuring SSL connection manager..."); HttpClientBuilder clientBuilder = HttpClients.custom(); SSLContext ctx = SocketCreatorFactory .getSocketCreatorForComponent(SecurableCommunicationChannel.WEB).getSslContext(); @@ -213,28 +270,42 @@ public boolean isRedirected( .setHostnameVerifier(NoopHostnameVerifier.INSTANCE) .build()) .build(); + System.err.println( + "[HTTP-INIT] SSL connection manager created: maxConnPerRoute=10, maxConnTotal=20"); clientBuilder.setConnectionManager(connectionManager); // Enable connection eviction to release stale connections + System.err + .println("[HTTP-INIT] Enabling connection eviction: expired immediately, idle after 10s"); clientBuilder.evictExpiredConnections(); clientBuilder.evictIdleConnections(TimeValue.ofSeconds(10)); // Jakarta EE migration: Create client with lenient redirect strategy clientBuilder.setRedirectStrategy(lenientRedirectStrategy); clientBuilder.setDefaultRequestConfig(requestConfig); + System.err.println("[HTTP-INIT] Building main HTTP client with SSL..."); httpClient = clientBuilder.build(); + System.err.println("[HTTP-INIT] Main HTTP client created successfully"); // Jakarta EE migration: Create client without redirect handling for login verification + System.err.println("[HTTP-INIT] Building no-redirect HTTP client..."); httpClientNoRedirect = HttpClients.custom() .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .disableRedirectHandling() .build(); + System.err.println("[HTTP-INIT] No-redirect HTTP client created successfully"); + System.err.println( + "[HTTP-INIT] HTTP client initialization completed at " + java.time.Instant.now()); } else { + System.err.println("[HTTP-INIT] Configuring non-SSL connection manager..."); // Configure connection pool for non-SSL client HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() .setMaxConnPerRoute(10) .setMaxConnTotal(20) .build(); + System.err.println( + "[HTTP-INIT] Non-SSL connection manager created: maxConnPerRoute=10, maxConnTotal=20"); // Jakarta EE migration: Create client with lenient redirect strategy + System.err.println("[HTTP-INIT] Building non-SSL HTTP clients..."); httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .setRedirectStrategy(lenientRedirectStrategy) @@ -248,6 +319,9 @@ public boolean isRedirected( .setDefaultRequestConfig(requestConfig) .disableRedirectHandling() .build(); + System.err.println("[HTTP-INIT] Non-SSL HTTP clients created successfully"); + System.err.println( + "[HTTP-INIT] HTTP client initialization completed at " + java.time.Instant.now()); } } diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java index b0dd2fe86bf..5df573a9a06 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseSecurityWithSSLTest.java @@ -95,22 +95,34 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { securityProps.setProperty(SSL_PROTOCOLS, "TLSv1.2"); securityProps.setProperty(SSL_CIPHERS, "any"); + System.err.println("[DEBUG] Starting locator with SSL configuration..."); + System.err.println("[DEBUG] SSL_PROTOCOLS: TLSv1.2"); + System.err.println("[DEBUG] SSL_CIPHERS: any"); locator.withSecurityManager(SimpleSecurityManager.class).withProperties(securityProps) .startLocator(); - System.err.println("[DEBUG] Locator started on port: " + locator.getHttpPort()); + System.err.println("[DEBUG] Locator started successfully on port: " + locator.getHttpPort()); + System.err.println("[DEBUG] Locator JMX port: " + locator.getJmxPort()); // Wait for JMX/Management Service to be fully initialized before login attempts // This prevents race conditions on slower CI machines where authentication may fail // if the JMX backend is not ready yet + System.err.println("[DEBUG] Getting ManagementService instance..."); ManagementService service = ManagementService.getExistingManagementService(locator.getLocator().getCache()); - System.err.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); + System.err.println("[DEBUG] ManagementService obtained: " + service); + System.err.println("[DEBUG] Waiting for MemberMXBean to be ready..."); + long mbeanWaitStart = System.currentTimeMillis(); await() .untilAsserted(() -> assertThat(service.getMemberMXBean()).isNotNull()); - System.err.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); + long mbeanWaitEnd = System.currentTimeMillis(); + System.err.println("[DEBUG] MemberMXBean is ready after " + (mbeanWaitEnd - mbeanWaitStart) + + "ms: " + service.getMemberMXBean()); + System.err + .println("[DEBUG] MemberMXBean class: " + service.getMemberMXBean().getClass().getName()); // Additionally wait for Pulse web application to be fully responsive System.err.println("[DEBUG] Waiting for Pulse web app to respond..."); + long webAppWaitStart = System.currentTimeMillis(); await() .untilAsserted(() -> { ClassicHttpResponse loginPageResponse = client.get("/pulse/login.html"); @@ -126,12 +138,20 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { } } }); - System.err.println("[DEBUG] Pulse web app is responsive"); + long webAppWaitEnd = System.currentTimeMillis(); + System.err.println( + "[DEBUG] Pulse web app is responsive after " + (webAppWaitEnd - webAppWaitStart) + "ms"); - System.err.println("[DEBUG] Attempting login with wrong password..."); + System.err.println("[DEBUG] ===== Testing wrong password login ====="); + System.err.println("[DEBUG] Attempting login with wrong password (data/wrongPassword)..."); + long wrongPasswordStart = System.currentTimeMillis(); ClassicHttpResponse response = client.loginToPulse("data", "wrongPassword"); - System.err.println("[DEBUG] Wrong password response: " + response.getCode() + ", Location: " - + (response.getFirstHeader("Location") != null + long wrongPasswordEnd = System.currentTimeMillis(); + System.err.println("[DEBUG] Wrong password response received in " + + (wrongPasswordEnd - wrongPasswordStart) + "ms"); + System.err.println("[DEBUG] Response code: " + response.getCode()); + System.err.println("[DEBUG] Location header: " + + (response.getFirstHeader("Location") != null ? response.getFirstHeader("Location").getValue() : "null")); assertThat(response.getCode()).isEqualTo(302); assertThat(response.getFirstHeader("Location").getValue()) @@ -139,17 +159,50 @@ public void loginWithIncorrectAndThenCorrectPassword() throws Exception { // Wait for JMX authentication backend to be ready by polling login attempts // The backend may timeout initially while JMX connections are being established + System.err.println("[DEBUG] ===== Starting JMX backend readiness check ====="); System.err.println("[DEBUG] Waiting for JMX authentication backend to be ready..."); + System.err.println("[DEBUG] Max wait time: 5 minutes, poll interval: 50ms"); + long jmxBackendWaitStart = System.currentTimeMillis(); + final java.util.concurrent.atomic.AtomicInteger attemptCounter = + new java.util.concurrent.atomic.AtomicInteger(0); await() .untilAsserted(() -> { + int attempt = attemptCounter.incrementAndGet(); + long attemptStartTime = System.currentTimeMillis(); + long elapsedSinceJmxStart = attemptStartTime - jmxBackendWaitStart; + System.err.println("[DEBUG] ========================================"); + System.err.println("[DEBUG] === Attempt #" + attempt + " ==="); + System.err.println("[DEBUG] Timestamp: " + java.time.Instant.now()); + System.err + .println("[DEBUG] Elapsed since JMX check start: " + elapsedSinceJmxStart + "ms"); System.err .println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); try { + long startTime = System.currentTimeMillis(); client.loginToPulseAndVerify("cluster", "cluster"); - System.err.println("[DEBUG] Login successful!"); + long duration = System.currentTimeMillis() - startTime; + System.err.println("[DEBUG] Login successful after " + duration + "ms!"); + long totalElapsed = System.currentTimeMillis() - jmxBackendWaitStart; + System.err.println("[DEBUG] Total JMX backend readiness time: " + totalElapsed + + "ms, total attempts: " + attempt); } catch (Exception e) { - System.err.println("[DEBUG] Login failed: " + e.getClass().getSimpleName() + ": " - + e.getMessage() + " - will retry"); + long duration = System.currentTimeMillis() - attemptStartTime; + System.err.println("[DEBUG] Login failed after " + duration + "ms: " + + e.getClass().getSimpleName() + ": " + e.getMessage()); + // Print root cause if different + Throwable cause = e.getCause(); + if (cause != null && cause != e) { + System.err.println("[DEBUG] Caused by: " + cause.getClass().getSimpleName() + ": " + + cause.getMessage()); + // Print second-level cause if present + Throwable secondLevelCause = cause.getCause(); + if (secondLevelCause != null && secondLevelCause != cause) { + System.err.println( + "[DEBUG] Caused by: " + secondLevelCause.getClass().getSimpleName() + ": " + + secondLevelCause.getMessage()); + } + } + System.err.println("[DEBUG] Will retry (attempt #" + attempt + ")..."); throw new AssertionError("Login failed, will retry", e); } }); @@ -187,19 +240,28 @@ public void loginWithDeprecatedSSLOptions() throws Exception { .startLocator(); System.err .println("[DEBUG] Locator started (deprecated SSL) on port: " + locator.getHttpPort()); + System.err.println("[DEBUG] Locator JMX port: " + locator.getJmxPort()); // Wait for JMX/Management Service to be fully initialized before login attempts // This prevents race conditions on slower CI machines where authentication may fail // if the JMX backend is not ready yet + System.err.println("[DEBUG] Getting ManagementService instance..."); ManagementService service = ManagementService.getExistingManagementService(locator.getLocator().getCache()); - System.err.println("[DEBUG] ManagementService obtained, waiting for MemberMXBean..."); + System.err.println("[DEBUG] ManagementService obtained: " + service); + System.err.println("[DEBUG] Waiting for MemberMXBean to be ready..."); + long mbeanWaitStart = System.currentTimeMillis(); await() .untilAsserted(() -> assertThat(service.getMemberMXBean()).isNotNull()); - System.err.println("[DEBUG] MemberMXBean is ready: " + service.getMemberMXBean()); + long mbeanWaitEnd = System.currentTimeMillis(); + System.err.println("[DEBUG] MemberMXBean is ready after " + (mbeanWaitEnd - mbeanWaitStart) + + "ms: " + service.getMemberMXBean()); + System.err + .println("[DEBUG] MemberMXBean class: " + service.getMemberMXBean().getClass().getName()); // Additionally wait for Pulse web application to be fully responsive System.err.println("[DEBUG] Waiting for Pulse web app to respond..."); + long webAppWaitStart = System.currentTimeMillis(); await() .untilAsserted(() -> { ClassicHttpResponse loginPageResponse = client.get("/pulse/login.html"); @@ -215,21 +277,56 @@ public void loginWithDeprecatedSSLOptions() throws Exception { } } }); - System.err.println("[DEBUG] Pulse web app is responsive"); + long webAppWaitEnd = System.currentTimeMillis(); + System.err.println( + "[DEBUG] Pulse web app is responsive after " + (webAppWaitEnd - webAppWaitStart) + "ms"); // Wait for JMX authentication backend to be ready by polling login attempts // The backend may timeout initially while JMX connections are being established + System.err.println("[DEBUG] ===== Starting JMX backend readiness check ====="); System.err.println("[DEBUG] Waiting for JMX authentication backend to be ready..."); + System.err.println("[DEBUG] Max wait time: 5 minutes, poll interval: 50ms"); + long jmxBackendWaitStart = System.currentTimeMillis(); + final java.util.concurrent.atomic.AtomicInteger attemptCounter2 = + new java.util.concurrent.atomic.AtomicInteger(0); await() .untilAsserted(() -> { + int attempt = attemptCounter2.incrementAndGet(); + long attemptStartTime = System.currentTimeMillis(); + long elapsedSinceJmxStart = attemptStartTime - jmxBackendWaitStart; + System.err.println("[DEBUG] ========================================"); + System.err.println("[DEBUG] === Attempt #" + attempt + " ==="); + System.err.println("[DEBUG] Timestamp: " + java.time.Instant.now()); + System.err + .println("[DEBUG] Elapsed since JMX check start: " + elapsedSinceJmxStart + "ms"); System.err .println("[DEBUG] Attempting login with correct credentials (cluster/cluster)..."); try { + long startTime = System.currentTimeMillis(); client.loginToPulseAndVerify("cluster", "cluster"); - System.err.println("[DEBUG] Login successful!"); + long duration = System.currentTimeMillis() - startTime; + System.err.println("[DEBUG] Login successful after " + duration + "ms!"); + long totalElapsed = System.currentTimeMillis() - jmxBackendWaitStart; + System.err.println("[DEBUG] Total JMX backend readiness time: " + totalElapsed + + "ms, total attempts: " + attempt); } catch (Exception e) { - System.err.println("[DEBUG] Login failed: " + e.getClass().getSimpleName() + ": " - + e.getMessage() + " - will retry"); + long duration = System.currentTimeMillis() - attemptStartTime; + System.err.println("[DEBUG] Login failed after " + duration + "ms: " + + e.getClass().getSimpleName() + ": " + e.getMessage()); + // Print root cause if different + Throwable cause = e.getCause(); + if (cause != null && cause != e) { + System.err.println("[DEBUG] Caused by: " + cause.getClass().getSimpleName() + ": " + + cause.getMessage()); + // Print second-level cause if present + Throwable secondLevelCause = cause.getCause(); + if (secondLevelCause != null && secondLevelCause != cause) { + System.err.println( + "[DEBUG] Caused by: " + secondLevelCause.getClass().getSimpleName() + ": " + + secondLevelCause.getMessage()); + } + } + System.err.println("[DEBUG] Will retry (attempt #" + attempt + ")..."); throw new AssertionError("Login failed, will retry", e); } }); From 3200252e1d9c23badf9f76fb91a5c87b89e4a78c Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Fri, 13 Feb 2026 05:59:54 -0500 Subject: [PATCH 13/16] Increase integrationTest heap to 2g to prevent OOM with extensive logging --- geode-assembly/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/geode-assembly/build.gradle b/geode-assembly/build.gradle index 0102ccea40c..c8b75fb5e99 100755 --- a/geode-assembly/build.gradle +++ b/geode-assembly/build.gradle @@ -579,6 +579,11 @@ tasks.named('distributedTest') { ] } +// Increase heap size for integrationTest to prevent OOM with Pulse SSL tests +tasks.named('integrationTest') { + maxHeapSize = '2g' +} + acceptanceTest.dependsOn(rootProject.getTasksByName("publishToMavenLocal", true)) installDist.dependsOn ':extensions:geode-modules-assembly:dist' From 6ca4bb419c592c32d3f5bfb60a1ff2faf942c117 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Fri, 13 Feb 2026 10:26:30 -0500 Subject: [PATCH 14/16] Add JMX connection and authentication debug logging - Added [JMX-CONNECT] logs in JMXDataUpdater.connect(): * Connection start time and thread name * Credentials type * SSL configuration * JMXConnectorFactory.connect() timing * Connection success/failure with duration * Multi-level exception chain on failure - Added [PULSE-AUTH] logs in GemFireAuthenticationProvider: * Authentication request timestamp * getClusterWithUserNameAndPassword() timing * Cluster connection status * JMXConnector null check result This will identify whether JMX connection or authentication is the bottleneck. --- .../pulse/internal/data/JMXDataUpdater.java | 21 +++++++++++++++++++ .../GemFireAuthenticationProvider.java | 12 +++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java index c73daab61ea..0f44429cbd0 100644 --- a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java @@ -173,6 +173,10 @@ private JmxManagerInfo getManagerInfoFromLocator(Repository repository) { */ @Override public JMXConnector connect(Object credentials) { + long connectStartTime = System.currentTimeMillis(); + System.err.println("[JMX-CONNECT] Starting JMX connection at " + java.time.Instant.now()); + System.err.println("[JMX-CONNECT] Thread: " + Thread.currentThread().getName()); + System.err.println("[JMX-CONNECT] Credentials type: " + (credentials != null ? credentials.getClass().getName() : "null")); try { String jmxSerURL = ""; @@ -220,9 +224,26 @@ public JMXConnector connect(Object credentials) { env.put("com.sun.jndi.rmi.factory.socket", new SslRMIClientSocketFactory()); } logger.info("Connecting to jmxURL : {}", jmxSerURL); + System.err.println("[JMX-CONNECT] Attempting JMXConnectorFactory.connect() to: " + jmxSerURL); + System.err.println("[JMX-CONNECT] With SSL: " + repository.isUseSSLManager()); + long jmxCallStart = System.currentTimeMillis(); conn = JMXConnectorFactory.connect(url, env); + long jmxCallEnd = System.currentTimeMillis(); + System.err.println("[JMX-CONNECT] JMXConnectorFactory.connect() successful in " + (jmxCallEnd - jmxCallStart) + "ms"); mbs = conn.getMBeanServerConnection(); cluster.setConnectedFlag(true); + long totalDuration = System.currentTimeMillis() - connectStartTime; + cluster.setConnectedFlag(false); + cluster.setConnectionErrorMsg(e.getMessage()); + System.err.println("[JMX-CONNECT] Connection FAILED after " + totalDuration + "ms"); + System.err.println("[JMX-CONNECT] Exception type: " + e.getClass().getName()); + System.err.println("[JMX-CONNECT] Exception message: " + e.getMessage()); + if (e.getCause() != null) { + System.err.println("[JMX-CONNECT] Caused by: " + e.getCause().getClass().getName() + ": " + e.getCause().getMessage()); + if (e.getCause().getCause() != null) { + System.err.println("[JMX-CONNECT] Caused by: " + e.getCause().getCause().getClass().getName() + ": " + e.getCause().getCause().getMessage()); + } + }nnection time: " + totalDuration + "ms"); } finally { System.setProperties(originalProperties); } diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java index f790c822b40..c4ceb738e78 100644 --- a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java @@ -63,11 +63,19 @@ public Authentication authenticate(Authentication authentication) throws Authent String password = authentication.getCredentials().toString(); logger.debug("Connecting to GemFire with user=" + name); - JMXConnector jmxc = - repository.getClusterWithUserNameAndPassword(name, password).getJMXConnector(); + System.err.println("[PULSE-AUTH] Authentication request for user: " + name + " at " + java.time.Instant.now()); + long authStartTime = System.currentTimeMillis(); + Cluster cluster = repository.getClusterWithUserNameAndPassword(name, password); + long authEndTime = System.currentTimeMillis(); + System.err.println("[PULSE-AUTH] getClusterWithUserNameAndPassword returned in " + (authEndTime - authStartTime) + "ms"); + System.err.println("[PULSE-AUTH] Cluster connected flag: " + (cluster != null && cluster.isConnectedFlag())); + System.err.println("[PULSE-AUTH] Cluster connection error: " + (cluster != null ? cluster.getConnectionErrorMsg() : "null")); + JMXConnector jmxc = cluster != null ? cluster.getJMXConnector() : null; if (jmxc == null) { + System.err.println("[PULSE-AUTH] JMXConnector is NULL - authentication FAILED for " + name); throw new BadCredentialsException("Error connecting to GemFire JMX Server"); } + System.err.println("[PULSE-AUTH] JMXConnector obtained - authentication SUCCESSFUL for " + name); Collection list = GemFireAuthentication.populateAuthorities(jmxc); GemFireAuthentication auth = new GemFireAuthentication(authentication.getPrincipal(), From 77375470916479e7e18e063ec0f56dbe0def8270 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Fri, 13 Feb 2026 10:39:18 -0500 Subject: [PATCH 15/16] Fix JMXDataUpdater code formatting and structure Fixed malformed code from previous commit: - Moved success-path logging to correct location - Moved failure-path logging to catch block properly - Removed corrupted text fragment - Applied spotless formatting --- .../pulse/internal/data/JMXDataUpdater.java | 30 +++++++++++-------- .../GemFireAuthenticationProvider.java | 15 ++++++---- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java index 0f44429cbd0..d923f502c07 100644 --- a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/data/JMXDataUpdater.java @@ -176,7 +176,8 @@ public JMXConnector connect(Object credentials) { long connectStartTime = System.currentTimeMillis(); System.err.println("[JMX-CONNECT] Starting JMX connection at " + java.time.Instant.now()); System.err.println("[JMX-CONNECT] Thread: " + Thread.currentThread().getName()); - System.err.println("[JMX-CONNECT] Credentials type: " + (credentials != null ? credentials.getClass().getName() : "null")); + System.err.println("[JMX-CONNECT] Credentials type: " + + (credentials != null ? credentials.getClass().getName() : "null")); try { String jmxSerURL = ""; @@ -224,14 +225,23 @@ public JMXConnector connect(Object credentials) { env.put("com.sun.jndi.rmi.factory.socket", new SslRMIClientSocketFactory()); } logger.info("Connecting to jmxURL : {}", jmxSerURL); - System.err.println("[JMX-CONNECT] Attempting JMXConnectorFactory.connect() to: " + jmxSerURL); + System.err + .println("[JMX-CONNECT] Attempting JMXConnectorFactory.connect() to: " + jmxSerURL); System.err.println("[JMX-CONNECT] With SSL: " + repository.isUseSSLManager()); long jmxCallStart = System.currentTimeMillis(); conn = JMXConnectorFactory.connect(url, env); long jmxCallEnd = System.currentTimeMillis(); - System.err.println("[JMX-CONNECT] JMXConnectorFactory.connect() successful in " + (jmxCallEnd - jmxCallStart) + "ms"); + System.err.println("[JMX-CONNECT] JMXConnectorFactory.connect() successful in " + + (jmxCallEnd - jmxCallStart) + "ms"); mbs = conn.getMBeanServerConnection(); cluster.setConnectedFlag(true); + long totalDuration = System.currentTimeMillis() - connectStartTime; + System.err.println("[JMX-CONNECT] Total connection time: " + totalDuration + "ms"); + } finally { + System.setProperties(originalProperties); + } + } + } catch (Exception e) { long totalDuration = System.currentTimeMillis() - connectStartTime; cluster.setConnectedFlag(false); cluster.setConnectionErrorMsg(e.getMessage()); @@ -239,18 +249,14 @@ public JMXConnector connect(Object credentials) { System.err.println("[JMX-CONNECT] Exception type: " + e.getClass().getName()); System.err.println("[JMX-CONNECT] Exception message: " + e.getMessage()); if (e.getCause() != null) { - System.err.println("[JMX-CONNECT] Caused by: " + e.getCause().getClass().getName() + ": " + e.getCause().getMessage()); + System.err.println("[JMX-CONNECT] Caused by: " + e.getCause().getClass().getName() + ": " + + e.getCause().getMessage()); if (e.getCause().getCause() != null) { - System.err.println("[JMX-CONNECT] Caused by: " + e.getCause().getCause().getClass().getName() + ": " + e.getCause().getCause().getMessage()); - } - }nnection time: " + totalDuration + "ms"); - } finally { - System.setProperties(originalProperties); + System.err + .println("[JMX-CONNECT] Caused by: " + e.getCause().getCause().getClass().getName() + + ": " + e.getCause().getCause().getMessage()); } } - } catch (Exception e) { - cluster.setConnectedFlag(false); - cluster.setConnectionErrorMsg(e.getMessage()); logger.fatal(e.getMessage(), e); if (conn != null) { try { diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java index c4ceb738e78..b9a99d6170f 100644 --- a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java @@ -63,19 +63,24 @@ public Authentication authenticate(Authentication authentication) throws Authent String password = authentication.getCredentials().toString(); logger.debug("Connecting to GemFire with user=" + name); - System.err.println("[PULSE-AUTH] Authentication request for user: " + name + " at " + java.time.Instant.now()); + System.err.println( + "[PULSE-AUTH] Authentication request for user: " + name + " at " + java.time.Instant.now()); long authStartTime = System.currentTimeMillis(); Cluster cluster = repository.getClusterWithUserNameAndPassword(name, password); long authEndTime = System.currentTimeMillis(); - System.err.println("[PULSE-AUTH] getClusterWithUserNameAndPassword returned in " + (authEndTime - authStartTime) + "ms"); - System.err.println("[PULSE-AUTH] Cluster connected flag: " + (cluster != null && cluster.isConnectedFlag())); - System.err.println("[PULSE-AUTH] Cluster connection error: " + (cluster != null ? cluster.getConnectionErrorMsg() : "null")); + System.err.println("[PULSE-AUTH] getClusterWithUserNameAndPassword returned in " + + (authEndTime - authStartTime) + "ms"); + System.err.println( + "[PULSE-AUTH] Cluster connected flag: " + (cluster != null && cluster.isConnectedFlag())); + System.err.println("[PULSE-AUTH] Cluster connection error: " + + (cluster != null ? cluster.getConnectionErrorMsg() : "null")); JMXConnector jmxc = cluster != null ? cluster.getJMXConnector() : null; if (jmxc == null) { System.err.println("[PULSE-AUTH] JMXConnector is NULL - authentication FAILED for " + name); throw new BadCredentialsException("Error connecting to GemFire JMX Server"); } - System.err.println("[PULSE-AUTH] JMXConnector obtained - authentication SUCCESSFUL for " + name); + System.err + .println("[PULSE-AUTH] JMXConnector obtained - authentication SUCCESSFUL for " + name); Collection list = GemFireAuthentication.populateAuthorities(jmxc); GemFireAuthentication auth = new GemFireAuthentication(authentication.getPrincipal(), From f993be50c5ad189b6bfb12bf67f675001ee2de78 Mon Sep 17 00:00:00 2001 From: Jinwoo Hwang Date: Fri, 13 Feb 2026 12:22:41 -0500 Subject: [PATCH 16/16] Add missing Cluster import to GemFireAuthenticationProvider --- .../pulse/internal/security/GemFireAuthenticationProvider.java | 1 + 1 file changed, 1 insertion(+) diff --git a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java index b9a99d6170f..1457e7632fe 100644 --- a/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java +++ b/geode-pulse/src/main/java/org/apache/geode/tools/pulse/internal/security/GemFireAuthenticationProvider.java @@ -30,6 +30,7 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component; +import org.apache.geode.tools.pulse.internal.data.Cluster; import org.apache.geode.tools.pulse.internal.data.Repository; /**