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

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


The following commit(s) were added to refs/heads/master by this push:
     new c15100698 feat(java): add TCP/TLS integration tests and examples 
(#2823)
c15100698 is described below

commit c15100698874c38f88f36a4bb62ba205c3e53db2
Author: Atharva Lade <[email protected]>
AuthorDate: Tue Mar 10 16:07:00 2026 -0500

    feat(java): add TCP/TLS integration tests and examples (#2823)
    
    Closes #2805
---
 .github/actions/java-gradle/pre-merge/action.yml   |  28 ++++
 examples/java/README.md                            |  22 +++
 examples/java/build.gradle.kts                     |   9 +
 .../examples/tcptls/consumer/TcpTlsConsumer.java   | 141 ++++++++++++++++
 .../examples/tcptls/producer/TcpTlsProducer.java   | 152 +++++++++++++++++
 .../client/blocking/tcp/TlsConnectionTest.java     | 183 +++++++++++++++++++++
 scripts/run-java-examples-from-readme.sh           |  81 ++++++++-
 7 files changed, 614 insertions(+), 2 deletions(-)

diff --git a/.github/actions/java-gradle/pre-merge/action.yml 
b/.github/actions/java-gradle/pre-merge/action.yml
index 55f0bcf59..47ccc4eb7 100644
--- a/.github/actions/java-gradle/pre-merge/action.yml
+++ b/.github/actions/java-gradle/pre-merge/action.yml
@@ -146,6 +146,34 @@ runs:
         pid-file: ${{ steps.iggy.outputs.pid_file }}
         log-file: ${{ steps.iggy.outputs.log_file }}
 
+    - name: Start Iggy server (TLS)
+      id: iggy-tls
+      if: inputs.task == 'test'
+      uses: ./.github/actions/utils/server-start
+      with:
+        pid-file: ${{ runner.temp }}/iggy-server-tls.pid
+        log-file: ${{ runner.temp }}/iggy-server-tls.log
+      env:
+        IGGY_TCP_TLS_ENABLED: "true"
+        IGGY_TCP_TLS_CERT_FILE: core/certs/iggy_cert.pem
+        IGGY_TCP_TLS_KEY_FILE: core/certs/iggy_key.pem
+
+    - name: TLS integration tests
+      if: inputs.task == 'test'
+      shell: bash
+      working-directory: foreign/java
+      env:
+        USE_EXTERNAL_SERVER: true
+        IGGY_TCP_TLS_ENABLED: "true"
+      run: ./gradlew :iggy:test --tests "*TlsConnectionTest*"
+
+    - name: Stop Iggy server (TLS)
+      if: always() && inputs.task == 'test'
+      uses: ./.github/actions/utils/server-stop
+      with:
+        pid-file: ${{ steps.iggy-tls.outputs.pid_file }}
+        log-file: ${{ steps.iggy-tls.outputs.log_file }}
+
     - name: Test Summary
       uses: test-summary/action@v2
       with:
diff --git a/examples/java/README.md b/examples/java/README.md
index 590eb3ad8..5058b1808 100644
--- a/examples/java/README.md
+++ b/examples/java/README.md
@@ -174,6 +174,28 @@ The async client uses Netty's event loop threads for I/O 
operations. **NEVER** b
 
 If your message processing involves blocking operations, offload to a separate 
thread pool using `thenApplyAsync(fn, executor)`.
 
+## Security Examples
+
+### TCP/TLS
+
+Demonstrates secure TLS-encrypted TCP connections:
+
+```bash
+./gradlew runTcpTlsProducer
+./gradlew runTcpTlsConsumer
+```
+
+These examples require a TLS-enabled Iggy server. Start the server with:
+
+```bash
+IGGY_TCP_TLS_ENABLED=true \
+IGGY_TCP_TLS_CERT_FILE=core/certs/iggy_cert.pem \
+IGGY_TCP_TLS_KEY_FILE=core/certs/iggy_key.pem \
+cargo r --bin iggy-server
+```
+
+Uses `IggyTcpClientBuilder` with TLS options (`enableTls`, `tlsDomain`, 
`tlsCaCertPath`) to establish TLS-encrypted TCP connections with CA certificate 
verification.
+
 ## Blocking vs. Async - When to Use Each
 
 The Iggy Java SDK provides two client types: **blocking (synchronous)** and 
**async (non-blocking)**. Choose based on your use case:
diff --git a/examples/java/build.gradle.kts b/examples/java/build.gradle.kts
index 95bfd195b..22ea68b97 100644
--- a/examples/java/build.gradle.kts
+++ b/examples/java/build.gradle.kts
@@ -110,3 +110,12 @@ tasks.register<JavaExec>("runAsyncConsumer") {
     mainClass.set("org.apache.iggy.examples.async.AsyncConsumer")
 }
 
+tasks.register<JavaExec>("runTcpTlsProducer") {
+    classpath = sourceSets["main"].runtimeClasspath
+    mainClass.set("org.apache.iggy.examples.tcptls.producer.TcpTlsProducer")
+}
+
+tasks.register<JavaExec>("runTcpTlsConsumer") {
+    classpath = sourceSets["main"].runtimeClasspath
+    mainClass.set("org.apache.iggy.examples.tcptls.consumer.TcpTlsConsumer")
+}
diff --git 
a/examples/java/src/main/java/org/apache/iggy/examples/tcptls/consumer/TcpTlsConsumer.java
 
b/examples/java/src/main/java/org/apache/iggy/examples/tcptls/consumer/TcpTlsConsumer.java
new file mode 100644
index 000000000..166dc3434
--- /dev/null
+++ 
b/examples/java/src/main/java/org/apache/iggy/examples/tcptls/consumer/TcpTlsConsumer.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iggy.examples.tcptls.consumer;
+
+import org.apache.iggy.client.blocking.tcp.IggyTcpClient;
+import org.apache.iggy.consumergroup.Consumer;
+import org.apache.iggy.identifier.StreamId;
+import org.apache.iggy.identifier.TopicId;
+import org.apache.iggy.message.Message;
+import org.apache.iggy.message.PolledMessages;
+import org.apache.iggy.message.PollingStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+/**
+ * TCP/TLS Consumer Example
+ *
+ * <p>Demonstrates consuming messages over a TLS-encrypted TCP connection
+ * using custom certificates from core/certs/.
+ *
+ * <p>Prerequisites: Start the Iggy server with TLS enabled:
+ * <pre>
+ *   IGGY_TCP_TLS_ENABLED=true \
+ *   IGGY_TCP_TLS_CERT_FILE=core/certs/iggy_cert.pem \
+ *   IGGY_TCP_TLS_KEY_FILE=core/certs/iggy_key.pem \
+ *   cargo r --bin iggy-server
+ * </pre>
+ */
+public final class TcpTlsConsumer {
+
+    private static final StreamId STREAM_ID = StreamId.of("tls-stream");
+    private static final TopicId TOPIC_ID = TopicId.of("tls-topic");
+
+    private static final long PARTITION_ID = 0L;
+
+    private static final int BATCHES_LIMIT = 5;
+
+    private static final long MESSAGES_PER_BATCH = 10L;
+    private static final long INTERVAL_MS = 500;
+
+    private static final Logger log = 
LoggerFactory.getLogger(TcpTlsConsumer.class);
+
+    private TcpTlsConsumer() {}
+
+    public static void main(String[] args) {
+        // Build a TCP client with TLS enabled.
+        // enableTls()           activates TLS on the TCP transport
+        // tlsCertificate(...)   points to the CA certificate used to verify 
the server cert
+        var client = IggyTcpClient.builder()
+                .host("localhost")
+                .port(8090)
+                .enableTls()
+                .tlsCertificate("../../core/certs/iggy_ca_cert.pem")
+                .credentials("iggy", "iggy")
+                .buildAndLogin();
+
+        consumeMessages(client);
+    }
+
+    private static void consumeMessages(IggyTcpClient client) {
+        log.info(
+                "Messages will be consumed from stream: {}, topic: {}, 
partition: {} with interval {}ms.",
+                STREAM_ID,
+                TOPIC_ID,
+                PARTITION_ID,
+                INTERVAL_MS);
+
+        BigInteger offset = BigInteger.ZERO;
+        int consumedBatches = 0;
+
+        Consumer consumer = Consumer.of(0L);
+
+        while (true) {
+            if (consumedBatches == BATCHES_LIMIT) {
+                log.info("Consumed {} batches of messages, exiting.", 
consumedBatches);
+                return;
+            }
+
+            try {
+                PolledMessages polledMessages = client.messages()
+                        .pollMessages(
+                                STREAM_ID,
+                                TOPIC_ID,
+                                Optional.of(PARTITION_ID),
+                                consumer,
+                                PollingStrategy.offset(offset),
+                                MESSAGES_PER_BATCH,
+                                false);
+
+                if (polledMessages.messages().isEmpty()) {
+                    log.info("No messages found.");
+                    Thread.sleep(INTERVAL_MS);
+                    continue;
+                }
+
+                for (Message message : polledMessages.messages()) {
+                    handleMessage(message, offset);
+                }
+
+                consumedBatches++;
+
+                offset = 
offset.add(BigInteger.valueOf(polledMessages.messages().size()));
+
+                Thread.sleep(INTERVAL_MS);
+
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                break;
+            } catch (Exception e) {
+                log.error("Error polling messages", e);
+                break;
+            }
+        }
+    }
+
+    private static void handleMessage(Message message, BigInteger offset) {
+        String payload = new String(message.payload(), StandardCharsets.UTF_8);
+        log.info("Handling message at offset {}, payload: {}...", offset, 
payload);
+    }
+}
diff --git 
a/examples/java/src/main/java/org/apache/iggy/examples/tcptls/producer/TcpTlsProducer.java
 
b/examples/java/src/main/java/org/apache/iggy/examples/tcptls/producer/TcpTlsProducer.java
new file mode 100644
index 000000000..8de2b277c
--- /dev/null
+++ 
b/examples/java/src/main/java/org/apache/iggy/examples/tcptls/producer/TcpTlsProducer.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iggy.examples.tcptls.producer;
+
+import org.apache.iggy.client.blocking.tcp.IggyTcpClient;
+import org.apache.iggy.identifier.StreamId;
+import org.apache.iggy.identifier.TopicId;
+import org.apache.iggy.message.Message;
+import org.apache.iggy.message.Partitioning;
+import org.apache.iggy.stream.StreamDetails;
+import org.apache.iggy.topic.CompressionAlgorithm;
+import org.apache.iggy.topic.TopicDetails;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static java.util.Optional.empty;
+
+/**
+ * TCP/TLS Producer Example
+ *
+ * <p>Demonstrates producing messages over a TLS-encrypted TCP connection
+ * using custom certificates from core/certs/.
+ *
+ * <p>Prerequisites: Start the Iggy server with TLS enabled:
+ * <pre>
+ *   IGGY_TCP_TLS_ENABLED=true \
+ *   IGGY_TCP_TLS_CERT_FILE=core/certs/iggy_cert.pem \
+ *   IGGY_TCP_TLS_KEY_FILE=core/certs/iggy_key.pem \
+ *   cargo r --bin iggy-server
+ * </pre>
+ */
+public final class TcpTlsProducer {
+
+    private static final String STREAM_NAME = "tls-stream";
+    private static final StreamId STREAM_ID = StreamId.of(STREAM_NAME);
+
+    private static final String TOPIC_NAME = "tls-topic";
+    private static final TopicId TOPIC_ID = TopicId.of(TOPIC_NAME);
+
+    private static final long PARTITION_ID = 0L;
+
+    private static final int BATCHES_LIMIT = 5;
+
+    private static final int MESSAGES_PER_BATCH = 10;
+    private static final long INTERVAL_MS = 500;
+
+    private static final Logger log = 
LoggerFactory.getLogger(TcpTlsProducer.class);
+
+    private TcpTlsProducer() {}
+
+    public static void main(String[] args) {
+        // Build a TCP client with TLS enabled.
+        // enableTls()           activates TLS on the TCP transport
+        // tlsCertificate(...)   points to the CA certificate used to verify 
the server cert
+        var client = IggyTcpClient.builder()
+                .host("localhost")
+                .port(8090)
+                .enableTls()
+                .tlsCertificate("../../core/certs/iggy_ca_cert.pem")
+                .credentials("iggy", "iggy")
+                .buildAndLogin();
+
+        createStream(client);
+        createTopic(client);
+        produceMessages(client);
+    }
+
+    private static void produceMessages(IggyTcpClient client) {
+        log.info(
+                "Messages will be sent to stream: {}, topic: {}, partition: {} 
with interval {}ms.",
+                STREAM_NAME,
+                TOPIC_NAME,
+                PARTITION_ID,
+                INTERVAL_MS);
+
+        int currentId = 0;
+        int sentBatches = 0;
+
+        Partitioning partitioning = Partitioning.partitionId(PARTITION_ID);
+
+        while (sentBatches < BATCHES_LIMIT) {
+            try {
+                Thread.sleep(INTERVAL_MS);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                break;
+            }
+
+            List<Message> messages = new ArrayList<>();
+            for (int i = 0; i < MESSAGES_PER_BATCH; i++) {
+                currentId++;
+                String payload = "message-" + currentId;
+                messages.add(Message.of(payload));
+            }
+
+            client.messages().sendMessages(STREAM_ID, TOPIC_ID, partitioning, 
messages);
+            sentBatches++;
+            log.info("Sent {} message(s).", MESSAGES_PER_BATCH);
+        }
+
+        log.info("Sent {} batches of messages, exiting.", sentBatches);
+    }
+
+    private static void createStream(IggyTcpClient client) {
+        Optional<StreamDetails> stream = client.streams().getStream(STREAM_ID);
+        if (stream.isPresent()) {
+            return;
+        }
+        client.streams().createStream(STREAM_NAME);
+        log.info("Stream {} was created.", STREAM_NAME);
+    }
+
+    private static void createTopic(IggyTcpClient client) {
+        Optional<TopicDetails> topic = client.topics().getTopic(STREAM_ID, 
TOPIC_ID);
+        if (topic.isPresent()) {
+            log.warn("Topic already exists and will not be created again.");
+            return;
+        }
+        client.topics()
+                .createTopic(
+                        STREAM_ID,
+                        1L,
+                        CompressionAlgorithm.None,
+                        BigInteger.ZERO,
+                        BigInteger.ZERO,
+                        empty(),
+                        TOPIC_NAME);
+        log.info("Topic {} was created.", TOPIC_NAME);
+    }
+}
diff --git 
a/foreign/java/java-sdk/src/test/java/org/apache/iggy/client/blocking/tcp/TlsConnectionTest.java
 
b/foreign/java/java-sdk/src/test/java/org/apache/iggy/client/blocking/tcp/TlsConnectionTest.java
new file mode 100644
index 000000000..e4605229e
--- /dev/null
+++ 
b/foreign/java/java-sdk/src/test/java/org/apache/iggy/client/blocking/tcp/TlsConnectionTest.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iggy.client.blocking.tcp;
+
+import org.apache.iggy.client.BaseIntegrationTest;
+import org.apache.iggy.identifier.StreamId;
+import org.apache.iggy.identifier.TopicId;
+import org.apache.iggy.message.Message;
+import org.apache.iggy.message.Partitioning;
+import org.apache.iggy.message.PolledMessages;
+import org.apache.iggy.message.PollingStrategy;
+import org.apache.iggy.stream.StreamDetails;
+import org.apache.iggy.topic.CompressionAlgorithm;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static java.util.Optional.empty;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Integration tests for TCP/TLS connections.
+ * Requires an externally started Iggy server with TLS enabled (like other 
SDKs).
+ *
+ * Enabled only when IGGY_TCP_TLS_ENABLED is set, indicating a TLS server is 
available.
+ */
+@EnabledIfEnvironmentVariable(named = "IGGY_TCP_TLS_ENABLED", matches = ".+")
+class TlsConnectionTest extends BaseIntegrationTest {
+
+    private static final Logger log = 
LoggerFactory.getLogger(TlsConnectionTest.class);
+    private static final Path CERTS_DIR = findCertsDir();
+    // The server certificate SAN is DNS:localhost, so TLS clients must connect
+    // via "localhost" to pass hostname verification (not 127.0.0.1).
+    private static final String TLS_HOST = "localhost";
+
+    @Test
+    void connectWithTlsWithCaCertShouldSucceed() {
+        var client = IggyTcpClient.builder()
+                .host(TLS_HOST)
+                .port(serverTcpPort())
+                .enableTls()
+                .tlsCertificate(CERTS_DIR.resolve("iggy_ca_cert.pem").toFile())
+                .credentials("iggy", "iggy")
+                .buildAndLogin();
+
+        var stats = client.system().getStats();
+        assertThat(stats).isNotNull();
+    }
+
+    @Test
+    void connectWithTlsWithServerCertShouldSucceed() {
+        var client = IggyTcpClient.builder()
+                .host(TLS_HOST)
+                .port(serverTcpPort())
+                .enableTls()
+                .tlsCertificate(CERTS_DIR.resolve("iggy_cert.pem").toFile())
+                .credentials("iggy", "iggy")
+                .buildAndLogin();
+
+        var stats = client.system().getStats();
+        assertThat(stats).isNotNull();
+    }
+
+    @Test
+    void connectWithoutTlsShouldFailWhenTlsRequired() {
+        // The blocking TCP client hangs on responses.take() when the server 
drops
+        // a non-TLS connection, so we run in a separate thread with a timeout.
+        var executor = Executors.newSingleThreadExecutor();
+        try {
+            var future = executor.submit(() -> {
+                var client = IggyTcpClient.builder()
+                        .host(serverHost())
+                        .port(serverTcpPort())
+                        .credentials("iggy", "iggy")
+                        .buildAndLogin();
+                client.system().getStats();
+            });
+            assertThatThrownBy(() -> future.get(10, TimeUnit.SECONDS))
+                    .isInstanceOfAny(ExecutionException.class, 
TimeoutException.class);
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test
+    void sendAndReceiveOverTlsShouldWork() {
+        var client = IggyTcpClient.builder()
+                .host(TLS_HOST)
+                .port(serverTcpPort())
+                .enableTls()
+                .tlsCertificate(CERTS_DIR.resolve("iggy_ca_cert.pem").toFile())
+                .credentials("iggy", "iggy")
+                .buildAndLogin();
+
+        String streamName = "tls-test-stream";
+        StreamId streamId = StreamId.of(streamName);
+        String topicName = "tls-test-topic";
+        TopicId topicId = TopicId.of(topicName);
+
+        try {
+            StreamDetails stream = client.streams().createStream(streamName);
+            assertThat(stream).isNotNull();
+
+            client.topics()
+                    .createTopic(
+                            streamId,
+                            1L,
+                            CompressionAlgorithm.None,
+                            BigInteger.ZERO,
+                            BigInteger.ZERO,
+                            empty(),
+                            topicName);
+
+            List<Message> messages =
+                    List.of(Message.of("tls-message-1"), 
Message.of("tls-message-2"), Message.of("tls-message-3"));
+
+            client.messages().sendMessages(streamId, topicId, 
Partitioning.partitionId(0L), messages);
+
+            PolledMessages polled = client.messages()
+                    .pollMessages(
+                            streamId,
+                            topicId,
+                            Optional.of(0L),
+                            org.apache.iggy.consumergroup.Consumer.of(0L),
+                            PollingStrategy.offset(BigInteger.ZERO),
+                            3L,
+                            false);
+
+            assertThat(polled.messages()).hasSize(3);
+        } finally {
+            try {
+                client.streams().deleteStream(streamId);
+            } catch (RuntimeException e) {
+                log.debug("Cleanup failed: {}", e.getMessage());
+            }
+        }
+    }
+
+    private static Path findCertsDir() {
+        File dir = new File(System.getProperty("user.dir"));
+        while (dir != null) {
+            File certs = new File(dir, "core/certs");
+            if (certs.isDirectory()
+                    && new File(certs, "iggy_cert.pem").exists()
+                    && new File(certs, "iggy_key.pem").exists()
+                    && new File(certs, "iggy_ca_cert.pem").exists()) {
+                return certs.toPath();
+            }
+            dir = dir.getParentFile();
+        }
+        throw new IllegalStateException(
+                "Could not find core/certs/ directory with TLS certificates. 
Run tests from the repository root.");
+    }
+}
diff --git a/scripts/run-java-examples-from-readme.sh 
b/scripts/run-java-examples-from-readme.sh
index 9fa768d6f..0c78cda2d 100755
--- a/scripts/run-java-examples-from-readme.sh
+++ b/scripts/run-java-examples-from-readme.sh
@@ -120,17 +120,94 @@ if [ -f "${README_FILE}" ]; then
 
         # Small delay between runs to avoid thrashing the server
         sleep 2
-    done < <(grep -E '^\./gradlew' "${README_FILE}")
+    done < <(grep -E '^\./gradlew' "${README_FILE}" | grep -v "TcpTls")
 else
     echo "README file ${README_FILE} not found in examples/java."
 fi
 
 cd "${ROOT_WORKDIR}"
 
-# Terminate server
+# Terminate non-TLS server
 kill -TERM "$(cat ${PID_FILE})"
 test -e ${PID_FILE} && rm ${PID_FILE}
 
+# Run TLS examples if non-TLS examples passed
+if [ "${exit_code}" -eq 0 ]; then
+    TLS_README="examples/java/README.md"
+    if [ -f "${TLS_README}" ] && grep -qE '^\./gradlew.*TcpTls' 
"${TLS_README}"; then
+        echo ""
+        echo "=== Running TLS examples ==="
+        echo ""
+
+        # Clean up for fresh TLS start
+        test -d local_data && rm -fr local_data
+        test -e ${LOG_FILE} && rm ${LOG_FILE}
+
+        # Start TLS server
+        echo "Starting TLS server from ${SERVER_BIN}..."
+        IGGY_ROOT_USERNAME=iggy IGGY_ROOT_PASSWORD=iggy \
+            IGGY_TCP_TLS_ENABLED=true \
+            IGGY_TCP_TLS_CERT_FILE=core/certs/iggy_cert.pem \
+            IGGY_TCP_TLS_KEY_FILE=core/certs/iggy_key.pem \
+            ${SERVER_BIN} &>${LOG_FILE} &
+        echo $! >${PID_FILE}
+
+        # Wait for TLS server to start
+        SERVER_START_TIME=0
+        while ! grep -q "has started" ${LOG_FILE}; do
+            if [ ${SERVER_START_TIME} -gt ${TIMEOUT} ]; then
+                echo "TLS server did not start within ${TIMEOUT} seconds."
+                ps fx
+                cat ${LOG_FILE}
+                exit_code=1
+                break
+            fi
+            echo "Waiting for TLS Iggy server to start... ${SERVER_START_TIME}"
+            sleep 1
+            ((SERVER_START_TIME += 1))
+        done
+
+        if [ "${exit_code}" -eq 0 ]; then
+            cd examples/java
+
+            while IFS= read -r command; do
+                # Remove backticks and comments from command
+                command=$(echo "${command}" | tr -d '`' | sed 's/^#.*//')
+                # Skip empty lines
+                if [ -z "${command}" ]; then
+                    continue
+                fi
+
+                echo -e "\e[33mChecking TLS example command:\e[0m ${command}"
+                echo ""
+
+                set +e
+                eval "${command}"
+                exit_code=$?
+                set -e
+
+                if [ ${exit_code} -ne 0 ]; then
+                    echo ""
+                    echo -e "\e[31mTLS example command failed:\e[0m ${command}"
+                    echo ""
+                    break
+                fi
+
+                # Small delay between runs to avoid thrashing the server
+                sleep 2
+            done < <(grep -E '^\./gradlew.*TcpTls' 
"${ROOT_WORKDIR}/${TLS_README}")
+
+            cd "${ROOT_WORKDIR}"
+        fi
+
+        # Terminate TLS server
+        if [ -e ${PID_FILE} ]; then
+            kill -TERM "$(cat ${PID_FILE})" 2>/dev/null || true
+            rm -f ${PID_FILE}
+        fi
+    fi
+fi
+
 if [ "${exit_code}" -eq 0 ]; then
     echo "Test passed"
 else

Reply via email to