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