diff --git a/examples/java/README.md b/examples/java/README.md
index 0e46d8ba62..395ca7d1a7 100644
--- a/examples/java/README.md
+++ b/examples/java/README.md
@@ -1,18 +1,14 @@
## Run
-Ensure that you have an instance of Valkey running on "localhost" on "6379". Otherwise, update glide.examples.ExamplesApp with a configuration that matches your server settings.
+Ensure that you have an instance of Valkey running on "localhost" on "6379". Otherwise, update glide.examples.StandaloneExample or glide.examples.ClusterExample with a configuration that matches your server settings.
-To run the example:
+To run the Standalone example:
```
cd valkey-glide/examples/java
-./gradlew :run
+./gradlew :runStandalone
```
-
-You should expect to see the output:
+To run the Cluster example:
```
-> Task :run
-PING: PONG
-PING(found you): found you
-SET(apples, oranges): OK
-GET(apples): oranges
+cd valkey-glide/examples/java
+./gradlew :runCluster
```
diff --git a/examples/java/build.gradle b/examples/java/build.gradle
index fa55ac434e..6ff2785725 100644
--- a/examples/java/build.gradle
+++ b/examples/java/build.gradle
@@ -1,6 +1,5 @@
plugins {
- // Apply the application plugin to add support for building a CLI application in Java.
- id 'application'
+ id "java"
id "com.google.osdetector" version "1.7.3"
}
@@ -11,10 +10,19 @@ repositories {
}
dependencies {
- implementation "io.valkey:valkey-glide:1.0.1:${osdetector.classifier}"
+ implementation "io.valkey:valkey-glide:1.+:${osdetector.classifier}"
}
-application {
- // Define the main class for the application.
- mainClass = 'glide.examples.ExamplesApp'
+task runStandalone(type: JavaExec) {
+ group = 'application'
+ description = 'Run the standalone example'
+ classpath = sourceSets.main.runtimeClasspath
+ mainClass = 'glide.examples.StandaloneExample'
+}
+
+task runCluster(type: JavaExec) {
+ group = 'application'
+ description = 'Run the cluster example'
+ classpath = sourceSets.main.runtimeClasspath
+ mainClass = 'glide.examples.ClusterExample'
}
diff --git a/examples/java/src/main/java/glide/examples/ClusterExample.java b/examples/java/src/main/java/glide/examples/ClusterExample.java
new file mode 100644
index 0000000000..cc598b632a
--- /dev/null
+++ b/examples/java/src/main/java/glide/examples/ClusterExample.java
@@ -0,0 +1,151 @@
+/** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
+package glide.examples;
+
+import static glide.api.logging.Logger.Level.ERROR;
+import static glide.api.logging.Logger.Level.INFO;
+import static glide.api.logging.Logger.Level.WARN;
+import static glide.api.logging.Logger.log;
+import static glide.api.models.configuration.RequestRoutingConfiguration.SimpleMultiNodeRoute.ALL_NODES;
+
+import glide.api.GlideClusterClient;
+import glide.api.logging.Logger;
+import glide.api.models.ClusterValue;
+import glide.api.models.commands.InfoOptions;
+import glide.api.models.configuration.GlideClusterClientConfiguration;
+import glide.api.models.configuration.NodeAddress;
+import glide.api.models.exceptions.ClosingException;
+import glide.api.models.exceptions.ConnectionException;
+import glide.api.models.exceptions.TimeoutException;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+public class ClusterExample {
+
+ /**
+ * Creates and returns a GlideClusterClient
instance.
+ *
+ *
This function initializes a GlideClusterClient
with the provided list of nodes.
+ * The list may contain the address of one or more cluster nodes, and the client will
+ * automatically discover all nodes in the cluster.
+ *
+ * @return A GlideClusterClient
connected to the discovered nodes.
+ * @throws CancellationException if the operation is cancelled.
+ * @throws ExecutionException if the client fails due to execution errors.
+ * @throws InterruptedException if the operation is interrupted.
+ */
+ public static GlideClusterClient createClient(List nodeList)
+ throws CancellationException, ExecutionException, InterruptedException {
+ // Check `GlideClusterClientConfiguration` for additional options.
+ GlideClusterClientConfiguration config =
+ GlideClusterClientConfiguration.builder()
+ .addresses(nodeList)
+ // Enable this field if the servers are configured with TLS.
+ // .useTLS(true);
+ .build();
+
+ GlideClusterClient client = GlideClusterClient.createClient(config).get();
+ return client;
+ }
+
+ /**
+ * Executes the main logic of the application, performing basic operations such as SET, GET, PING,
+ * and INFO REPLICATION using the provided GlideClusterClient
.
+ *
+ * @param client An instance of GlideClusterClient
.
+ * @throws ExecutionException if an execution error occurs during operations.
+ * @throws InterruptedException if the operation is interrupted.
+ */
+ public static void appLogic(GlideClusterClient client)
+ throws ExecutionException, InterruptedException {
+
+ // Send SET and GET
+ CompletableFuture setResponse = client.set("foo", "bar");
+ log(INFO, "app", "Set response is " + setResponse.get());
+
+ CompletableFuture getResponse = client.get("foo");
+ log(INFO, "app", "Get response is " + getResponse.get());
+
+ // Send PING to all primaries (according to Valkey's PING request_policy)
+ CompletableFuture pong = client.ping();
+ log(INFO, "app", "Ping response is " + pong.get());
+
+ // Send INFO REPLICATION with routing option to all nodes
+ ClusterValue infoResponse =
+ client
+ .info(InfoOptions.builder().section(InfoOptions.Section.REPLICATION).build(), ALL_NODES)
+ .get();
+ log(
+ INFO,
+ "app",
+ "INFO REPLICATION responses from all nodes are " + infoResponse.getMultiValue());
+ }
+
+ /**
+ * Executes the application logic with exception handling.
+ *
+ * @throws ExecutionException if an execution error occurs during operations.
+ */
+ private static void execAppLogic() throws ExecutionException {
+
+ // Define list of nodes
+ List nodeList =
+ Collections.singletonList(NodeAddress.builder().host("localhost").port(6379).build());
+
+ while (true) {
+ try (GlideClusterClient client = createClient(nodeList)) {
+ appLogic(client);
+ return;
+ } catch (CancellationException e) {
+ log(ERROR, "glide", "Request cancelled: " + e.getMessage());
+ throw e;
+ } catch (InterruptedException e) {
+ log(ERROR, "glide", "Client interrupted: " + e.getMessage());
+ Thread.currentThread().interrupt(); // Restore interrupt status
+ throw new CancellationException("Client was interrupted.");
+ } catch (ExecutionException e) {
+ // All Glide errors will be handled as ExecutionException
+ if (e.getCause() instanceof ClosingException) {
+ // If the error message contains "NOAUTH", raise the exception
+ // because it indicates a critical authentication issue.
+ if (e.getMessage().contains("NOAUTH")) {
+ log(ERROR, "glide", "Authentication error encountered: " + e.getMessage());
+ throw e;
+ } else {
+ log(WARN, "glide", "Client has closed and needs to be re-created: " + e.getMessage());
+ }
+ } else if (e.getCause() instanceof ConnectionException) {
+ // The client wasn't able to reestablish the connection within the given retries
+ log(ERROR, "glide", "Connection error encountered: " + e.getMessage());
+ throw e;
+ } else if (e.getCause() instanceof TimeoutException) {
+ // A request timed out. You may choose to retry the execution based on your application's
+ // logic
+ log(ERROR, "glide", "Timeout encountered: " + e.getMessage());
+ throw e;
+ } else {
+ log(ERROR, "glide", "Execution error encountered: " + e.getCause());
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * The entry point of the cluster example. This method sets up the logger configuration and
+ * executes the main application logic.
+ *
+ * @param args Command-line arguments passed to the application.
+ * @throws ExecutionException if an error occurs during execution of the application logic.
+ */
+ public static void main(String[] args) throws ExecutionException {
+ // In this example, we will utilize the client's logger for all log messages
+ Logger.setLoggerConfig(INFO);
+ // Optional - set the logger to write to a file
+ // Logger.setLoggerConfig(Logger.Level.INFO, file)
+ execAppLogic();
+ }
+}
diff --git a/examples/java/src/main/java/glide/examples/ExamplesApp.java b/examples/java/src/main/java/glide/examples/ExamplesApp.java
deleted file mode 100644
index 4a686786eb..0000000000
--- a/examples/java/src/main/java/glide/examples/ExamplesApp.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
-package glide.examples;
-
-import glide.api.GlideClient;
-import glide.api.models.configuration.GlideClientConfiguration;
-import glide.api.models.configuration.NodeAddress;
-import java.util.concurrent.ExecutionException;
-
-public class ExamplesApp {
-
- // main application entrypoint
- public static void main(String[] args) {
- runGlideExamples();
- }
-
- private static void runGlideExamples() {
- String host = "localhost";
- Integer port = 6379;
- boolean useSsl = false;
-
- GlideClientConfiguration config =
- GlideClientConfiguration.builder()
- .address(NodeAddress.builder().host(host).port(port).build())
- .useTLS(useSsl)
- .build();
-
- try (GlideClient client = GlideClient.createClient(config).get()) {
-
- System.out.println("PING: " + client.ping().get());
- System.out.println("PING(found you): " + client.ping("found you").get());
-
- System.out.println("SET(apples, oranges): " + client.set("apples", "oranges").get());
- System.out.println("GET(apples): " + client.get("apples").get());
-
- } catch (ExecutionException | InterruptedException e) {
- System.out.println("Glide example failed with an exception: ");
- e.printStackTrace();
- }
- }
-}
diff --git a/examples/java/src/main/java/glide/examples/StandaloneExample.java b/examples/java/src/main/java/glide/examples/StandaloneExample.java
new file mode 100644
index 0000000000..996e408e83
--- /dev/null
+++ b/examples/java/src/main/java/glide/examples/StandaloneExample.java
@@ -0,0 +1,138 @@
+/** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
+package glide.examples;
+
+import static glide.api.logging.Logger.Level.ERROR;
+import static glide.api.logging.Logger.Level.INFO;
+import static glide.api.logging.Logger.Level.WARN;
+import static glide.api.logging.Logger.log;
+
+import glide.api.GlideClient;
+import glide.api.logging.Logger;
+import glide.api.models.configuration.GlideClientConfiguration;
+import glide.api.models.configuration.NodeAddress;
+import glide.api.models.exceptions.ClosingException;
+import glide.api.models.exceptions.ConnectionException;
+import glide.api.models.exceptions.TimeoutException;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+public class StandaloneExample {
+
+ /**
+ * Creates and returns a GlideClient
instance.
+ *
+ * This function initializes a GlideClient
with the provided list of nodes. The
+ * list may contain either only primary node or a mix of primary and replica nodes. The
+ * GlideClient
+ *
use these nodes to connect to the Standalone setup servers.
+ *
+ * @return A GlideClient
connected to the provided node address.
+ * @throws CancellationException if the operation is cancelled.
+ * @throws ExecutionException if the client fails due to execution errors.
+ * @throws InterruptedException if the operation is interrupted.
+ */
+ public static GlideClient createClient(List nodeList)
+ throws CancellationException, ExecutionException, InterruptedException {
+ // Check `GlideClientConfiguration` for additional options.
+ GlideClientConfiguration config =
+ GlideClientConfiguration.builder()
+ .addresses(nodeList)
+ // Enable this field if the servers are configured with TLS.
+ // .useTLS(true);
+ .build();
+
+ GlideClient client = GlideClient.createClient(config).get();
+ return client;
+ }
+
+ /**
+ * Executes the main logic of the application, performing basic operations such as SET, GET, and
+ * PING using the provided GlideClient
.
+ *
+ * @param client An instance of GlideClient
.
+ * @throws ExecutionException if an execution error occurs during operations.
+ * @throws InterruptedException if the operation is interrupted.
+ */
+ public static void appLogic(GlideClient client) throws ExecutionException, InterruptedException {
+
+ // Send SET and GET
+ CompletableFuture setResponse = client.set("foo", "bar");
+ log(INFO, "app", "Set response is " + setResponse.get());
+
+ CompletableFuture getResponse = client.get("foo");
+ log(INFO, "app", "Get response is " + getResponse.get());
+
+ // Send PING
+ CompletableFuture pong = client.ping();
+ log(INFO, "app", "Ping response is " + pong.get());
+ }
+
+ /**
+ * Executes the application logic with exception handling.
+ *
+ * @throws ExecutionException if an execution error occurs during operations.
+ */
+ private static void execAppLogic() throws ExecutionException {
+
+ // Define list of nodes
+ List nodeList =
+ Collections.singletonList(NodeAddress.builder().host("localhost").port(6379).build());
+
+ while (true) {
+ try (GlideClient client = createClient(nodeList)) {
+ appLogic(client);
+ return;
+ } catch (CancellationException e) {
+ log(ERROR, "glide", "Request cancelled: " + e.getMessage());
+ throw e;
+ } catch (InterruptedException e) {
+ log(ERROR, "glide", "Client interrupted: " + e.getMessage());
+ Thread.currentThread().interrupt(); // Restore interrupt status
+ throw new CancellationException("Client was interrupted.");
+ } catch (ExecutionException e) {
+ // All Glide errors will be handled as ExecutionException
+ if (e.getCause() instanceof ClosingException) {
+ // If the error message contains "NOAUTH", raise the exception
+ // because it indicates a critical authentication issue.
+ if (e.getMessage().contains("NOAUTH")) {
+ log(ERROR, "glide", "Authentication error encountered: " + e.getMessage());
+ throw e;
+ } else {
+ log(WARN, "glide", "Client has closed and needs to be re-created: " + e.getMessage());
+ }
+ } else if (e.getCause() instanceof ConnectionException) {
+ // The client wasn't able to reestablish the connection within the given retries
+ log(ERROR, "glide", "Connection error encountered: " + e.getMessage());
+ throw e;
+ } else if (e.getCause() instanceof TimeoutException) {
+ // A request timed out. You may choose to retry the execution based on your application's
+ // logic
+ log(ERROR, "glide", "Timeout encountered: " + e.getMessage());
+ throw e;
+ } else {
+ log(ERROR, "glide", "Execution error encountered: " + e.getCause());
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * The entry point of the standalone example. This method sets up the logger configuration and
+ * executes the main application logic.
+ *
+ * @param args Command-line arguments passed to the application.
+ * @throws ExecutionException if an error occurs during execution of the application logic.
+ */
+ public static void main(String[] args) throws ExecutionException {
+ // In this example, we will utilize the client's logger for all log messages
+ Logger.setLoggerConfig(INFO);
+ // Optional - set the logger to write to a file
+ // Logger.setLoggerConfig(Logger.Level.INFO, file)
+ execAppLogic();
+ }
+}