From 86d9ea2abc514734d0509255fa896e8b7e64b9a0 Mon Sep 17 00:00:00 2001 From: mtmk Date: Thu, 10 Jul 2025 16:25:13 +0100 Subject: [PATCH 001/135] chaos runner native image docs --- chaos-runner/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index 1d62aec..48c526d 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -71,6 +71,15 @@ java -cp / io.synadia.chaos.ChaosRunner --servers 1 --delay 4 Alternatively you can run a program like the [ChaosRunnerExample](src/examples/java/io/synadia/examples/ChaosRunnerExample.java) from an ide. +### Running native image + +You can use [GraalVM](https://www.graalvm.org/) native-image to create native executable for your platform: +``` +# install GraalVM +> native-image.cmd -cp C:\Users\mtmk\Downloads\chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner +> .\chaos-runner.exe --servers 1 --delay 4000 --initial 10000 +``` + --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. From 52428f25a0cf674e017bacf155e39c7e5424998f Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 10 Jul 2025 13:27:31 -0400 Subject: [PATCH 002/135] chaos runner native image docs --- chaos-runner/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index 48c526d..9735a7a 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -76,7 +76,7 @@ Alternatively you can run a program like the [ChaosRunnerExample](src/examples/j You can use [GraalVM](https://www.graalvm.org/) native-image to create native executable for your platform: ``` # install GraalVM -> native-image.cmd -cp C:\Users\mtmk\Downloads\chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner +> native-image.cmd -cp \chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner > .\chaos-runner.exe --servers 1 --delay 4000 --initial 10000 ``` From fb3a2c8089f221458259f8b5ef57677cbf8de8e5 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 10 Jul 2025 13:38:47 -0400 Subject: [PATCH 003/135] switch starting port to 4222 instead of 4220 --- chaos-runner/README.md | 40 ++++++++++--------- .../java/io/synadia/chaos/ChaosArguments.java | 7 ++-- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index 9735a7a..df6a978 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -25,25 +25,27 @@ You can get this jar in 2 ways. ## Command Line Arguments -| Argument | Description | Default | -|----------------------|----------------------------------------------------------------------------|-------------| -| `--servers ` | Number of servers. Accepts 1, 3 or 5 | 3 | -| `--delay ` | Delay to bring down a server, since all servers were up. | 5000 | -| `--initial ` | The first delay. Gives time to start your test program and run setup. | 30000 | -| `--down ` | Delay to bring a server up once it is brought down. | 5000 | -| `--cname ` | Cluster name. Ignored for 1 server. | "cluster" | -| `--prefix ` | Prefix to use for the server name. Used in it's entirety for 1 server | "server" | -| `--dir ` | The working dir. Used as the parent dir for JetStream storage directories. | _temp_ | -| `--nojs` | Do not run the server with JetStream. JetStream is on by default. | JetStream | -| `--random` | Take the servers down randomly. Default is Round Robin. | Round Robin | -| `--port` | The starting server port. | 4220 | -| `--listen` | The starting listen port for clusters. | 4230 | +| Argument | Description | Default | +|-------------------------|----------------------------------------------------------------------------|---------------| +| `--servers <1, 3 or 5>` | Number of servers. Accepts 1, 3 or 5 | 3 | +| `--delay ` | Delay to bring down a server, starting when all servers are up. | 5000 | +| `--initial ` | The first delay. Gives time to start your test program and run setup. | 30000 | +| `--down ` | Delay to bring a server up once it is brought down. | 5000 | +| `--cname ` | Cluster name. Ignored for 1 server. | "cluster" | +| `--prefix ` | Prefix to use for the server name. Used in it's entirety for 1 server | "server" | +| `--dir ` | The working dir. Used as the parent dir for JetStream storage directories. | _system temp_ | +| `--nojs` | Do not run the server with JetStream. JetStream is on by default. | JetStream | +| `--random` | Take the servers down randomly. Default is Round Robin. | Round Robin | +| `--port` | The starting server port. | 4222 | +| `--listen` | The starting listen port for clusters. | 4232 | #### Regarding ports Given any starting port, the system automatically figures the ports for the other nodes. -For example for 3 nodes: -* if the starting server port is 4220, the other ports are 4221 and 4222. -* if the listen port is 4230, the other listen ports are 4231 and 4232 +For 1 node, the port and listen are used directly. For 3 or 5 nodes used for the first server, +each server port is 1 more than the last. Make sure that starting and listen won't overlap. +So for example, for 3 servers... +* If the starting server port is 4222, the other ports are 4223 and 4224. +* If the listen port is 4232, the other listen ports are 4232 and 4233 ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) @@ -73,9 +75,11 @@ Alternatively you can run a program like the [ChaosRunnerExample](src/examples/j ### Running native image -You can use [GraalVM](https://www.graalvm.org/) native-image to create native executable for your platform: +You can use [GraalVM](https://www.graalvm.org/) native-image to create native executable for your platform +* Assumes you've installed graalvm +* You may need the path to the cmd if not already in your path. + ``` -# install GraalVM > native-image.cmd -cp \chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner > .\chaos-runner.exe --servers 1 --delay 4000 --initial 10000 ``` diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java index 64ccd51..48783b1 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java @@ -4,7 +4,8 @@ import java.nio.file.Path; import java.nio.file.Paths; -import static io.nats.NatsRunnerUtils.*; +import static io.nats.NatsRunnerUtils.DEFAULT_CLUSTER_NAME; +import static io.nats.NatsRunnerUtils.DEFAULT_SERVER_NAME_PREFIX; public class ChaosArguments { @@ -17,8 +18,8 @@ public class ChaosArguments { long delay = 5_000; long downTime = 5_000; boolean random = false; - int port = DEFAULT_PORT_START; - int listen = DEFAULT_LISTEN_START; + int port = 4222; + int listen = port + 10; public ChaosArguments servers(int servers) { this.servers = servers; From c674dbfce7ad1c878b61872f71177432a2f18e09 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 10 Jul 2025 13:40:51 -0400 Subject: [PATCH 004/135] switch starting port to 4222 instead of 4220 --- chaos-runner/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 54a861d..d697a86 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -40,7 +40,7 @@ repositories { dependencies { implementation 'io.nats:jnats-server-runner:2.0.2-SNAPSHOT' - implementation 'io.nats:jnats:2.21.4' // this is only for the example + implementation 'io.nats:jnats:2.21.4' // this is only for the example and the uber jar won't include it testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' From 0c5277b446030e5d2daf6e38961833959ef3b484 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 10 Jul 2025 13:42:42 -0400 Subject: [PATCH 005/135] switch starting port to 4222 instead of 4220 --- chaos-runner/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index d697a86..3cb6107 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats-server-runner:2.0.2-SNAPSHOT' + implementation 'io.nats:jnats-server-runner:2.0.2' implementation 'io.nats:jnats:2.21.4' // this is only for the example and the uber jar won't include it From c822ac0441a6fb490709b3aa3a3790577ddea43d Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 10 Jul 2025 13:57:49 -0400 Subject: [PATCH 006/135] chaos runner native image docs --- chaos-runner/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index df6a978..afbbfb1 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -73,11 +73,16 @@ java -cp / io.synadia.chaos.ChaosRunner --servers 1 --delay 4 Alternatively you can run a program like the [ChaosRunnerExample](src/examples/java/io/synadia/examples/ChaosRunnerExample.java) from an ide. -### Running native image +### Native image -You can use [GraalVM](https://www.graalvm.org/) native-image to create native executable for your platform -* Assumes you've installed graalvm -* You may need the path to the cmd if not already in your path. +You can a download zip file containing a Windows executable `chaos-runner.exe` from the release page, +[chaos-runner.zip](https://github.com/synadia-io/orbit.java/releases/download/cr%2F0.0.2/chaos-runner.zip) + +-or- + +You can use [GraalVM](https://www.graalvm.org/) native-image to create a native executable for your platform. +This assumes you've installed graalvm. You may need to specify the ful path to the native-image.cmd +(or your platform equivalent) if not already in your path. ``` > native-image.cmd -cp \chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner From 34f0ee15d221bf5b05e599266e18c27b23c6e99b Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 11 Jul 2025 09:15:35 -0400 Subject: [PATCH 007/135] better example and added monitor port --- chaos-runner/README.md | 1 + .../synadia/examples/ChaosRunnerExample.java | 73 ++++++++++++++++--- .../java/io/synadia/chaos/ChaosArguments.java | 11 ++- .../java/io/synadia/chaos/ChaosRunner.java | 14 +++- .../java/io/synadia/chaos/ChaosUtils.java | 6 +- 5 files changed, 89 insertions(+), 16 deletions(-) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index afbbfb1..ea1b713 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -38,6 +38,7 @@ You can get this jar in 2 ways. | `--random` | Take the servers down randomly. Default is Round Robin. | Round Robin | | `--port` | The starting server port. | 4222 | | `--listen` | The starting listen port for clusters. | 4232 | +| `--monitor` | The starting monitor port. | 4282 | #### Regarding ports Given any starting port, the system automatically figures the ports for the other nodes. diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java index e6dbda1..ac62948 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java @@ -6,26 +6,34 @@ import io.nats.client.Connection; import io.nats.client.Nats; import io.nats.client.Options; +import io.nats.client.api.ServerInfo; import io.synadia.chaos.ChaosArguments; import io.synadia.chaos.ChaosRunner; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; import java.util.ArrayList; import java.util.List; import static io.synadia.chaos.ChaosUtils.report; public class ChaosRunnerExample { - static final int NUM_CONNECTIONS = 2; + private static final int NUM_CONNECTIONS = 2; - static final int SERVER_COUNT = 3; // 1, 3, 5 - static final long DELAY = 5000; // the delay to bring a server down - static final long INITIAL_DELAY = 10000; // the delay to bring a server down the first time - static final long DOWN_TIME = 5000; // how long before bringing the server up - static final long STAY_ALIVE = 60_000; // how long to run the example program + private static final int SERVER_COUNT = 5; // 1, 3, 5 + private static final long DELAY = 5000; // the delay to bring a server down + private static final long INITIAL_DELAY = 10000; // the delay to bring a server down the first time + private static final long DOWN_TIME = 5000; // how long before bringing the server up + private static final int HEALTH_CHECK_DELAY = 3000; public static void main(String[] args) throws Exception { ChaosArguments arguments = new ChaosArguments() .servers(SERVER_COUNT) + .serverNamePrefix("cr-example-server") + .clusterName("cr-example-cluster") .delay(DELAY) .initialDelay(INITIAL_DELAY) .downTime(DOWN_TIME); @@ -36,9 +44,9 @@ public static void main(String[] args) throws Exception { Thread.sleep(1000); String[] urls = runner.getConnectionUrls(); - report("EXAMPLE", "Connection Urls:"); + report("Connection Urls"); for (String url : urls) { - report("EXAMPLE", " " + url); + report(" ", url); } List connections = new ArrayList<>(urls.length); @@ -51,11 +59,52 @@ public static void main(String[] args) throws Exception { Connection connection = Nats.connect(options); connections.add(connection); - report("EXAMPLE", "Initial connection for " + cn, "Port: " + connection.getServerInfo().getPort()); + ServerInfo si = connection.getServerInfo(); + report("Initial connection", cn, si.getPort(), si.getCluster()); } - // this just allows time for the runner to work - Thread.sleep(STAY_ALIVE); - System.exit(0); + int[] ports = runner.getConnectionPorts(); + int[] monitorPorts = runner.getMonitorPorts(); + + while (true) { + Thread.sleep(HEALTH_CHECK_DELAY); + report("HealthZ"); + for (int i = 0; i < monitorPorts.length; i++) { + int port = ports[i]; + int mport = monitorPorts[i]; + report(" ", port + "/" + mport, readHealthz(mport)); + } + } + } + + private static String readHealthz(int port) { + return readEndpoint(port, "healthz"); + } + + private static String readEndpoint(int port, String endpoint) { + String sUrl = "http://localhost:" + port + "/" + endpoint; + try { + URL url = new URL(sUrl); + InputStream inputStream = url.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + + boolean first = true; + String line; + StringBuilder content = new StringBuilder(); + while ((line = reader.readLine()) != null) { + if (first) { + first = false; + } + else { + content.append(System.lineSeparator()); + } + content.append(line); + } + reader.close(); + return content.toString().trim(); + } + catch (IOException e) { + return e.getMessage(); + } } } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java index 48783b1..da17dcc 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java @@ -19,7 +19,8 @@ public class ChaosArguments { long downTime = 5_000; boolean random = false; int port = 4222; - int listen = port + 10; + int listen = 4232; + int monitor = 4282; public ChaosArguments servers(int servers) { this.servers = servers; @@ -84,6 +85,11 @@ public ChaosArguments listen(int listen) { return this; } + public ChaosArguments monitor(int monitor) { + this.monitor = monitor; + return this; + } + public ChaosArguments args(String[] args) { if (args != null && args.length > 0) { try { @@ -123,6 +129,9 @@ public ChaosArguments args(String[] args) { case "--listen": listen(Integer.parseInt(args[++x])); break; + case "--monitor": + monitor(Integer.parseInt(args[++x])); + break; case "": break; default: diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index c027033..952900a 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -34,6 +34,7 @@ public class ChaosRunner { public final boolean random; public final int port; public final int listen; + public final int monitor; private final List clusterInserts; private final List natsServerRunners; @@ -60,6 +61,14 @@ public int[] getConnectionPorts() { return ports; } + public int[] getMonitorPorts() { + int[] ports = new int[servers]; + for (int ix = 0; ix < servers; ix++) { + ports[ix] = clusterInserts.get(ix).node.monitor; + } + return ports; + } + public String[] getConnectionUrls() { String[] urls = new String[servers]; for (int ix = 0; ix < servers; ix++) { @@ -171,6 +180,7 @@ else if (!a.workDirectory.toFile().exists()) { this.random = a.random; this.port = a.port; this.listen = a.listen; + this.monitor = a.monitor; natsServerRunners = new ArrayList<>(); if (servers == 1) { @@ -183,6 +193,7 @@ else if (!a.workDirectory.toFile().exists()) { Path jsStorePath = Paths.get(jsStoreDirBase.toString(), "" + port); cn = ClusterNode.builder() .port(port) + .monitor(monitor) .jsStoreDir(jsStorePath) .build(); @@ -193,6 +204,7 @@ else if (!a.workDirectory.toFile().exists()) { else { storeDir = storeDir.replace("\\", "/"); } + inserts.add("http: " + monitor); inserts.add("jetstream {"); inserts.add(" store_dir=" + storeDir); inserts.add("}"); @@ -203,7 +215,7 @@ else if (!a.workDirectory.toFile().exists()) { clusterInserts.add(new ClusterInsert(cn, inserts.toArray(new String[0]))); } else { - List cns = createNodes(servers, clusterName, serverNamePrefix, jsStoreDirBase, DEFAULT_HOST, port, listen, null); + List cns = createNodes(servers, clusterName, serverNamePrefix, jsStoreDirBase, DEFAULT_HOST, port, listen, monitor); clusterInserts = createClusterInserts(cns); } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java index 9f3649c..502b153 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java @@ -31,8 +31,10 @@ public static void report(String label, Object... parts) { String prefix = "[" + System.currentTimeMillis() + "] " + label; StringBuilder sb = new StringBuilder(prefix); for (Object part : parts) { - sb.append(" | "); - sb.append(part); + if (part != null) { + sb.append(" | "); + sb.append(part); + } } System.out.println(sb); } From 0b91682da47f944feeefe0cb72aa4aa2277ec0fb Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 11 Jul 2025 16:56:05 -0400 Subject: [PATCH 008/135] Monitor is optional; improved example --- chaos-runner/README.md | 4 +- chaos-runner/build.gradle | 14 ++++++ chaos-runner/make.bat | 2 + .../examples/ChaosConnectionListener.java | 36 ++++++++++++++-- .../synadia/examples/ChaosErrorListener.java | 11 ++--- .../synadia/examples/ChaosRunnerExample.java | 36 +++++++++------- .../java/io/synadia/chaos/ChaosRunner.java | 39 ++++++++++------- .../java/io/synadia/chaos/ChaosUtils.java | 43 ++++++++++++++----- 8 files changed, 135 insertions(+), 50 deletions(-) create mode 100644 chaos-runner/make.bat diff --git a/chaos-runner/README.md b/chaos-runner/README.md index ea1b713..94bcb31 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -38,7 +38,7 @@ You can get this jar in 2 ways. | `--random` | Take the servers down randomly. Default is Round Robin. | Round Robin | | `--port` | The starting server port. | 4222 | | `--listen` | The starting listen port for clusters. | 4232 | -| `--monitor` | The starting monitor port. | 4282 | +| `--monitor` | The starting monitor port. use 0 for no monitor | 4282 | #### Regarding ports Given any starting port, the system automatically figures the ports for the other nodes. @@ -86,7 +86,7 @@ This assumes you've installed graalvm. You may need to specify the ful path to t (or your platform equivalent) if not already in your path. ``` -> native-image.cmd -cp \chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner +> native-image.cmd --install-exit-handlers -cp \chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner > .\chaos-runner.exe --servers 1 --delay 4000 --initial 10000 ``` diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 3cb6107..6088806 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -140,6 +140,20 @@ tasks.register ('uberJar', Jar) { exclude 'META-INF/*.RSA','META-INF/*.SF','META-INF/*.DSA','**/examples**','placeholder.*' } +tasks.register ('examplesUberJar', Jar) { + archiveClassifier.set('examplesUber') + from sourceSets.main.output + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath + .findAll { + it.name.contains('jnats') || it.name.contains('nats-server-runner') // examples needs jnats too + } + .collect { zipTree(it) } + } + exclude 'META-INF/*.RSA','META-INF/*.SF','META-INF/*.DSA','placeholder.*' +} + jacoco { toolVersion = "0.8.6" } diff --git a/chaos-runner/make.bat b/chaos-runner/make.bat new file mode 100644 index 0000000..6be1fb9 --- /dev/null +++ b/chaos-runner/make.bat @@ -0,0 +1,2 @@ +call gradle clean uberJar examplesUberJar +native-image.cmd --install-exit-handlers -cp build\libs\chaos-runner-0.0.3-SNAPSHOT-uber.jar io.synadia.chaos.ChaosRunner chaos-runner \ No newline at end of file diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java index cbe926b..b4a846c 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java @@ -6,17 +6,47 @@ import io.nats.client.Connection; import io.nats.client.ConnectionListener; +import java.util.concurrent.atomic.AtomicInteger; + import static io.synadia.chaos.ChaosUtils.report; public class ChaosConnectionListener implements ConnectionListener { - private final String reportLabel; + private static String message(Events event) { + switch (event) { + case CONNECTED: return "Connected"; + case CLOSED: return "Closed"; + case DISCONNECTED: return "Disconnected"; + case RECONNECTED: return "Re-Connected"; + case RESUBSCRIBED: return "Subscriptions Re-Established"; + case DISCOVERED_SERVERS: return "Servers Discovered"; + case LAME_DUCK: return "Entering lame duck mode"; + } + return ""; + }; + + private final String connectionName; + private final AtomicInteger currentPort; public ChaosConnectionListener(String connectionName) { - this.reportLabel = "CL/" + connectionName; + this.connectionName = connectionName; + currentPort = new AtomicInteger(0); } @Override public void connectionEvent(Connection conn, Events type) { - report(reportLabel, type); + int cur; + if (type == Events.CONNECTED) { + cur = conn.getServerInfo().getPort(); + currentPort.set(cur); + } + else { + cur = currentPort.get(); + } + if (cur == 0) { + report("CL", connectionName, message(type)); + } + else { + report("CL", connectionName, message(type), "Port: " + cur); + } } } diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java index b76cf71..228302f 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java @@ -13,20 +13,20 @@ import static io.synadia.chaos.ChaosUtils.report; public class ChaosErrorListener extends ErrorListenerConsoleImpl { - private final String reportLabel; + private final String connectionName; public ChaosErrorListener(String connectionName) { - this.reportLabel = "EL/" + connectionName; + this.connectionName = connectionName; } @Override public void errorOccurred(Connection conn, String error) { - report(reportLabel, supplyMessage("[SEVERE] errorOccurred", conn, null, null, "Error: ", error)); + report("EL", connectionName, "Error", error); } @Override public void exceptionOccurred(Connection conn, Exception exp) { - report(reportLabel, supplyMessage("[SEVERE] exceptionOccurred", conn, null, null, "Exception: ", exp)); + report("EL", connectionName, "Exception", exp); } @Override @@ -39,7 +39,7 @@ public void messageDiscarded(Connection conn, Message msg) { @Override public void heartbeatAlarm(Connection conn, JetStreamSubscription sub, long lastStreamSequence, long lastConsumerSequence) { - report(reportLabel, supplyMessage("[SEVERE] heartbeatAlarm", conn, null, sub, "lastStreamSequence: ", lastStreamSequence, "lastConsumerSequence: ", lastConsumerSequence)); + report("EL", connectionName, "Heartbeat Alarm", "Last Stream Sequence: " + lastStreamSequence, "Last Consumer Sequence: " + lastConsumerSequence); } @Override @@ -60,5 +60,6 @@ public void flowControlProcessed(Connection conn, JetStreamSubscription sub, Str @Override public void socketWriteTimeout(Connection conn) { + report("EL", connectionName, "Socket Write Timeout"); } } diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java index ac62948..87393f7 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java @@ -6,7 +6,6 @@ import io.nats.client.Connection; import io.nats.client.Nats; import io.nats.client.Options; -import io.nats.client.api.ServerInfo; import io.synadia.chaos.ChaosArguments; import io.synadia.chaos.ChaosRunner; @@ -21,22 +20,26 @@ import static io.synadia.chaos.ChaosUtils.report; public class ChaosRunnerExample { - private static final int NUM_CONNECTIONS = 2; - - private static final int SERVER_COUNT = 5; // 1, 3, 5 + private static final int SERVER_COUNT = 3; // 1, 3, 5 private static final long DELAY = 5000; // the delay to bring a server down private static final long INITIAL_DELAY = 10000; // the delay to bring a server down the first time private static final long DOWN_TIME = 5000; // how long before bringing the server up private static final int HEALTH_CHECK_DELAY = 3000; + private static final int NUM_CONNECTIONS = 5; + public static void main(String[] args) throws Exception { ChaosArguments arguments = new ChaosArguments() .servers(SERVER_COUNT) + .workDirectory("C:\\temp\\chaos-runner") .serverNamePrefix("cr-example-server") .clusterName("cr-example-cluster") .delay(DELAY) .initialDelay(INITIAL_DELAY) - .downTime(DOWN_TIME); + .downTime(DOWN_TIME) + // this is done last so anything on the command line + // is used over the hard coded items. + .args(args); ChaosRunner runner = ChaosRunner.start(arguments); @@ -49,30 +52,33 @@ public static void main(String[] args) throws Exception { report(" ", url); } + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") List connections = new ArrayList<>(urls.length); for (int i = 0; i < NUM_CONNECTIONS; i++) { - String cn = "CONN/" + i; + String connectionName = "Conn" + (i + 1); Options options = Options.builder().servers(urls) - .connectionListener(new ChaosConnectionListener(cn)) - .errorListener(new ChaosErrorListener(cn)) + .connectionListener(new ChaosConnectionListener(connectionName)) + .errorListener(new ChaosErrorListener(connectionName)) .build(); Connection connection = Nats.connect(options); connections.add(connection); - ServerInfo si = connection.getServerInfo(); - report("Initial connection", cn, si.getPort(), si.getCluster()); } int[] ports = runner.getConnectionPorts(); int[] monitorPorts = runner.getMonitorPorts(); + boolean hasMonitor = monitorPorts[0] > 0; + String[] reports = new String[ports.length]; while (true) { Thread.sleep(HEALTH_CHECK_DELAY); - report("HealthZ"); - for (int i = 0; i < monitorPorts.length; i++) { - int port = ports[i]; - int mport = monitorPorts[i]; - report(" ", port + "/" + mport, readHealthz(mport)); + if (hasMonitor) { + report("HealthZ"); + for (int i = 0; i < monitorPorts.length; i++) { + int port = ports[i]; + int mport = monitorPorts[i]; + report(" ", port + "/" + mport, readHealthz(mport)); + } } } } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index 952900a..cda878a 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -61,12 +61,21 @@ public int[] getConnectionPorts() { return ports; } + public int[] getListenPorts() { + int[] lports = new int[servers]; + for (int ix = 0; ix < servers; ix++) { + lports[ix] = clusterInserts.get(ix).node.listen; + } + return lports; + } + public int[] getMonitorPorts() { - int[] ports = new int[servers]; + int[] mports = new int[servers]; for (int ix = 0; ix < servers; ix++) { - ports[ix] = clusterInserts.get(ix).node.monitor; + Integer mport = clusterInserts.get(ix).node.monitor; + mports[ix] = mport == null ? 0 : mport; } - return ports; + return mports; } public String[] getConnectionUrls() { @@ -186,17 +195,18 @@ else if (!a.workDirectory.toFile().exists()) { if (servers == 1) { List inserts = new ArrayList<>(); ClusterNode cn; - if (jsStoreDirBase == null) { - cn = null; + Path jsStorePath = Paths.get(jsStoreDirBase.toString(), "" + port); + cn = ClusterNode.builder() + .port(port) + .listen(listen) + .monitor(monitor < 1 ? null : monitor) + .jsStoreDir(jsStorePath) + .build(); + + if (monitor > 0) { + inserts.add("http: " + monitor); } - else { - Path jsStorePath = Paths.get(jsStoreDirBase.toString(), "" + port); - cn = ClusterNode.builder() - .port(port) - .monitor(monitor) - .jsStoreDir(jsStorePath) - .build(); - + if (js) { String storeDir = jsStorePath.toString(); if (File.separatorChar == '\\') { storeDir = storeDir.replace("\\", "\\\\").replace("/", "\\\\"); @@ -204,7 +214,6 @@ else if (!a.workDirectory.toFile().exists()) { else { storeDir = storeDir.replace("\\", "/"); } - inserts.add("http: " + monitor); inserts.add("jetstream {"); inserts.add(" store_dir=" + storeDir); inserts.add("}"); @@ -215,7 +224,7 @@ else if (!a.workDirectory.toFile().exists()) { clusterInserts.add(new ClusterInsert(cn, inserts.toArray(new String[0]))); } else { - List cns = createNodes(servers, clusterName, serverNamePrefix, jsStoreDirBase, DEFAULT_HOST, port, listen, monitor); + List cns = createNodes(servers, clusterName, serverNamePrefix, jsStoreDirBase, DEFAULT_HOST, port, listen, monitor < 1 ? null : monitor); clusterInserts = createClusterInserts(cns); } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java index 502b153..95fd993 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java @@ -12,21 +12,44 @@ public static String toString(ChaosRunner r) { public static String toString(ChaosRunner r, String sep, String prefix, String indent, String outdent) { String spi = sep + prefix + indent; StringBuilder sb = new StringBuilder(prefix).append("Chaos Runner:"); - sb.append(spi).append("servers=").append(r.servers).append(outdent) - .append(spi).append("js=").append(r.js).append(outdent); - if (r.servers > 1) { - sb.append(spi).append("clusterName=").append(r.clusterName).append(outdent) - .append(spi).append("serverNamePrefix=").append(r.serverNamePrefix).append(outdent); + sb.append(spi).append("servers=").append(r.servers).append(outdent); + if (r.servers == 1) { + sb.append(spi).append("serverName=").append(r.serverNamePrefix).append(outdent); + sb.append(spi).append("port=").append(r.port).append(outdent); + sb.append(spi).append("listen=").append(r.listen).append(outdent); + sb.append(spi).append("monitor=").append(r.monitor).append(outdent); + sb.append(spi).append("url=").append(r.getConnectionUrls()[0]).append(outdent); } - sb.append(spi).append("jsStoreDirBase=").append(r.jsStoreDirBase).append(outdent) - .append(spi).append("initialDelay=").append(r.initialDelay).append(outdent) - .append(spi).append("delay=").append(r.delay).append(outdent) - .append(spi).append("downTime=").append(r.downTime).append(outdent) - .append(spi).append("random=").append(r.random); + else { + sb.append(spi).append("clusterName=").append(r.clusterName).append(outdent); + sb.append(spi).append("serverNamePrefix=").append(r.serverNamePrefix).append(outdent); + sb.append(spi).append("ports=").append(stringify(r.getConnectionPorts())).append(outdent); + sb.append(spi).append("listen=").append(stringify(r.getListenPorts())).append(outdent); + sb.append(spi).append("monitor=").append(stringify(r.getMonitorPorts())).append(outdent); + } + sb.append(spi).append("js=").append(r.js).append(outdent); + if (r.js) { + sb.append(spi).append("jsStoreDirBase=").append(r.jsStoreDirBase).append(outdent); + } + sb.append(spi).append("initialDelay=").append(r.initialDelay).append(outdent); + sb.append(spi).append("delay=").append(r.delay).append(outdent); + sb.append(spi).append("downTime=").append(r.downTime).append(outdent); + sb.append(spi).append("random=").append(r.random); return sb.toString(); } + private static StringBuilder stringify(int[] ints) { + StringBuilder sb = new StringBuilder(); + for (int j = 0, intsLength = ints.length; j < intsLength; j++) { + if (j > 0) { + sb.append(','); + } + sb.append(ints[j]); + } + return sb; + } + public static void report(String label, Object... parts) { String prefix = "[" + System.currentTimeMillis() + "] " + label; StringBuilder sb = new StringBuilder(prefix); From 0da488d5f51d19689278592be103f54ab9af96fd Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 11 Jul 2025 16:57:01 -0400 Subject: [PATCH 009/135] Monitor is optional; improved example --- chaos-runner/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index 94bcb31..abd0aa3 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -38,7 +38,7 @@ You can get this jar in 2 ways. | `--random` | Take the servers down randomly. Default is Round Robin. | Round Robin | | `--port` | The starting server port. | 4222 | | `--listen` | The starting listen port for clusters. | 4232 | -| `--monitor` | The starting monitor port. use 0 for no monitor | 4282 | +| `--monitor` | The starting monitor port. Use 0 for no monitor | 4282 | #### Regarding ports Given any starting port, the system automatically figures the ports for the other nodes. From 5fc54873455ab5083a21b30fab085bbebb16e2fb Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 11 Jul 2025 17:05:54 -0400 Subject: [PATCH 010/135] Monitor is optional; improved example --- chaos-runner/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index abd0aa3..2405f83 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -82,12 +82,12 @@ You can a download zip file containing a Windows executable `chaos-runner.exe` f -or- You can use [GraalVM](https://www.graalvm.org/) native-image to create a native executable for your platform. -This assumes you've installed graalvm. You may need to specify the ful path to the native-image.cmd +This assumes you've installed graalvm. You may need to specify the full path to the native-image.cmd (or your platform equivalent) if not already in your path. ``` > native-image.cmd --install-exit-handlers -cp \chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner -> .\chaos-runner.exe --servers 1 --delay 4000 --initial 10000 +> chaos-runner.exe --servers 1 --delay 4000 --initial 10000 ``` --- From 589c1fd3d5cd7e98acba39b87133774d498f1e82 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 11 Jul 2025 17:13:53 -0400 Subject: [PATCH 011/135] Start 0.0.4 --- chaos-runner/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/chaos-runner/README.md b/chaos-runner/README.md index 2405f83..3a51430 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -5,8 +5,8 @@ A simple java program that can start 1 or more NATS Servers and then add chaos, by taking one of them down on a delay and bringing it back up after a downtime. -**Current Release**: 0.0.2 -  **Current Snapshot**: 0.0.3-SNAPSHOT +**Current Release**: 0.0.3 +  **Current Snapshot**: 0.0.4-SNAPSHOT   **Gradle and Maven** `io.synadia:chaos-runner` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) @@ -16,11 +16,11 @@ by taking one of them down on a delay and bringing it back up after a downtime. The project builds an Uber Jar that contains the compiled code for the Chaos Runner and the Nats Server Runner. You can get this jar in 2 ways. -1. Download the release: [chaos-runner-0.0.2-uber.jar](https://repo1.maven.org/maven2/io/synadia/chaos-runner/0.0.2/chaos-runner-0.0.2-uber.jar) +1. Download the release: [chaos-runner-0.0.3-uber.jar](https://repo1.maven.org/maven2/io/synadia/chaos-runner/0.0.3/chaos-runner-0.0.3-uber.jar) 2. Build from the source. Get the entire chaos-runner source from this Orbit repo, and from the chaos-runner project directory and run `gradle uberJar` - The Uber Jar `chaos-runner-0.0.2-SNAPSHOT-uber.jar` will appear in the `build/libs/` directory + The Uber Jar `chaos-runner-0.0.3-SNAPSHOT-uber.jar` will appear in the `build/libs/` directory (relative to the `chaos-runner` project directory.) ## Command Line Arguments @@ -64,11 +64,11 @@ java -cp / io.synadia.chaos.ChaosRunner --servers 1 --delay 4 #### Path-To and Jar-Name 1\.If you downloaded the Uber Jar release: * the `` will be wherever you stored the file. -* The `` will be `chaos-runner-0.0.2-uber.jar`. +* The `` will be `chaos-runner-0.0.3-uber.jar`. 2\. If you build it yourself: * the `` will be relative to the `chaos-runner` directory in `build/libs` -* the `` will be `chaos-runner-0.0.2-SNAPSHOT-uber.jar`. +* the `` will be `chaos-runner-0.0.3-SNAPSHOT-uber.jar`. ## Other ways to run... @@ -77,7 +77,7 @@ Alternatively you can run a program like the [ChaosRunnerExample](src/examples/j ### Native image You can a download zip file containing a Windows executable `chaos-runner.exe` from the release page, -[chaos-runner.zip](https://github.com/synadia-io/orbit.java/releases/download/cr%2F0.0.2/chaos-runner.zip) +[chaos-runner-003-windows-exe.zip](https://github.com/synadia-io/orbit.java/releases/download/cr%2F0.0.3/chaos-runner-003-windows-exe.zip) -or- @@ -86,7 +86,7 @@ This assumes you've installed graalvm. You may need to specify the full path to (or your platform equivalent) if not already in your path. ``` -> native-image.cmd --install-exit-handlers -cp \chaos-runner-0.0.2-uber.jar io.synadia.chaos.ChaosRunner chaos-runner +> native-image.cmd --install-exit-handlers -cp \chaos-runner-0.0.3-uber.jar io.synadia.chaos.ChaosRunner chaos-runner > chaos-runner.exe --servers 1 --delay 4000 --initial 10000 ``` From c662b65022555552ae26d12edf69f70c7afb844c Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 11 Jul 2025 17:14:10 -0400 Subject: [PATCH 012/135] Start 0.0.4 --- chaos-runner/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 6088806..4351bea 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.3" +def jarVersion = "0.0.4" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From 2bce106dc86dd3c1c6772204cd09fe2b6ef6ab0b Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 11 Jul 2025 17:25:48 -0400 Subject: [PATCH 013/135] Start 0.0.4 --- .../synadia/examples/ChaosRunnerExample.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java index 87393f7..87baf24 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java @@ -69,15 +69,25 @@ public static void main(String[] args) throws Exception { int[] monitorPorts = runner.getMonitorPorts(); boolean hasMonitor = monitorPorts[0] > 0; - String[] reports = new String[ports.length]; + String[] hzs = new String[ports.length]; while (true) { Thread.sleep(HEALTH_CHECK_DELAY); if (hasMonitor) { - report("HealthZ"); + boolean changed = false; for (int i = 0; i < monitorPorts.length; i++) { - int port = ports[i]; - int mport = monitorPorts[i]; - report(" ", port + "/" + mport, readHealthz(mport)); + String hz = readHealthz(monitorPorts[i]); + if (!hz.equals(hzs[i])) { + changed = true; + hzs[i] = hz; + } + } + if (changed) { + report("HealthZ"); + for (int i = 0; i < monitorPorts.length; i++) { + int port = ports[i]; + int mport = monitorPorts[i]; + report(" ", port + "/" + mport, hzs[i]); + } } } } From 0ff99e3da3301e03994f2afad3ac4e4aabd5752d Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 14 Jul 2025 16:14:02 -0400 Subject: [PATCH 014/135] Make it easier to embed --- .../java/io/synadia/chaos/ChaosArguments.java | 60 +++++++++++++++++++ .../java/io/synadia/chaos/ChaosRunner.java | 8 ++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java index da17dcc..7b5e1fd 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java @@ -43,14 +43,26 @@ public ChaosArguments js(boolean js) { } public ChaosArguments workDirectory(String workDirectory) { + if (workDirectory == null || workDirectory.trim().isEmpty()) { + this.workDirectory = null; + return this; + } return workDirectory(Paths.get(workDirectory)); } public ChaosArguments workDirectory(File workDirectory) { + if (workDirectory == null) { + this.workDirectory = null; + return this; + } return workDirectory(workDirectory.getPath()); } public ChaosArguments workDirectory(Path workDirectory) { + if (workDirectory == null) { + this.workDirectory = null; + return this; + } this.workDirectory = workDirectory; return this; } @@ -151,4 +163,52 @@ public void error(String errMsg) { System.err.println("ERROR: " + errMsg); System.exit(-1); } + + public int getServers() { + return servers; + } + + public String getClusterName() { + return clusterName; + } + + public String getServerNamePrefix() { + return serverNamePrefix; + } + + public boolean isJs() { + return js; + } + + public Path getWorkDirectory() { + return workDirectory; + } + + public long getInitialDelay() { + return initialDelay; + } + + public long getDelay() { + return delay; + } + + public long getDownTime() { + return downTime; + } + + public boolean isRandom() { + return random; + } + + public int getPort() { + return port; + } + + public int getListen() { + return listen; + } + + public int getMonitor() { + return monitor; + } } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index cda878a..042945f 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -257,6 +257,10 @@ public static void main(String[] args) { } public static ChaosRunner start(ChaosArguments a) { + return start(a, true); + } + + public static ChaosRunner start(ChaosArguments a, boolean print) { NatsServerRunner.setDefaultOutputLevel(Level.SEVERE); try { INSTANCE = new ChaosRunner(a); @@ -265,7 +269,9 @@ public static ChaosRunner start(ChaosArguments a) { System.out.println("Failed to start ChaosRunner: " + e.getMessage()); System.exit(-1); } - System.out.println(ChaosUtils.toString(INSTANCE, System.lineSeparator(), "[" + System.currentTimeMillis() + "] ", " ", "")); + if (print) { + System.out.println(ChaosUtils.toString(INSTANCE, System.lineSeparator(), "[" + System.currentTimeMillis() + "] ", " ", "")); + } Runtime.getRuntime().addShutdownHook( new Thread("app-shutdown-hook") { From 3e0bb6a111560ec6c8743baae436fbc41e40118a Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 15 Jul 2025 08:22:20 -0400 Subject: [PATCH 015/135] Make it easier to embed --- chaos-runner/build.gradle | 2 +- .../examples/ChaosConnectionListener.java | 6 +- .../synadia/examples/ChaosErrorListener.java | 10 ++-- .../synadia/examples/ChaosRunnerExample.java | 10 ++-- .../java/io/synadia/chaos/ChaosPrinter.java | 9 +++ .../java/io/synadia/chaos/ChaosRunner.java | 59 ++++++++++--------- .../java/io/synadia/chaos/ChaosUtils.java | 51 +++++++++++++--- 7 files changed, 96 insertions(+), 51 deletions(-) create mode 100644 chaos-runner/src/main/java/io/synadia/chaos/ChaosPrinter.java diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 4351bea..9a93d45 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.4" +def jarVersion = "0.0.5" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java index b4a846c..0440949 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosConnectionListener.java @@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicInteger; -import static io.synadia.chaos.ChaosUtils.report; +import static io.synadia.chaos.ChaosUtils.out; public class ChaosConnectionListener implements ConnectionListener { private static String message(Events event) { @@ -43,10 +43,10 @@ public void connectionEvent(Connection conn, Events type) { cur = currentPort.get(); } if (cur == 0) { - report("CL", connectionName, message(type)); + out("CL", connectionName, message(type)); } else { - report("CL", connectionName, message(type), "Port: " + cur); + out("CL", connectionName, message(type), "Port: " + cur); } } } diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java index 228302f..ec8538d 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosErrorListener.java @@ -10,7 +10,7 @@ import io.nats.client.impl.ErrorListenerConsoleImpl; import io.nats.client.support.Status; -import static io.synadia.chaos.ChaosUtils.report; +import static io.synadia.chaos.ChaosUtils.out; public class ChaosErrorListener extends ErrorListenerConsoleImpl { private final String connectionName; @@ -21,12 +21,12 @@ public ChaosErrorListener(String connectionName) { @Override public void errorOccurred(Connection conn, String error) { - report("EL", connectionName, "Error", error); + out("EL", connectionName, "Error", error); } @Override public void exceptionOccurred(Connection conn, Exception exp) { - report("EL", connectionName, "Exception", exp); + out("EL", connectionName, "Exception", exp); } @Override @@ -39,7 +39,7 @@ public void messageDiscarded(Connection conn, Message msg) { @Override public void heartbeatAlarm(Connection conn, JetStreamSubscription sub, long lastStreamSequence, long lastConsumerSequence) { - report("EL", connectionName, "Heartbeat Alarm", "Last Stream Sequence: " + lastStreamSequence, "Last Consumer Sequence: " + lastConsumerSequence); + out("EL", connectionName, "Heartbeat Alarm", "Last Stream Sequence: " + lastStreamSequence, "Last Consumer Sequence: " + lastConsumerSequence); } @Override @@ -60,6 +60,6 @@ public void flowControlProcessed(Connection conn, JetStreamSubscription sub, Str @Override public void socketWriteTimeout(Connection conn) { - report("EL", connectionName, "Socket Write Timeout"); + out("EL", connectionName, "Socket Write Timeout"); } } diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java index 87baf24..45b357c 100644 --- a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerExample.java @@ -17,7 +17,7 @@ import java.util.ArrayList; import java.util.List; -import static io.synadia.chaos.ChaosUtils.report; +import static io.synadia.chaos.ChaosUtils.out; public class ChaosRunnerExample { private static final int SERVER_COUNT = 3; // 1, 3, 5 @@ -47,9 +47,9 @@ public static void main(String[] args) throws Exception { Thread.sleep(1000); String[] urls = runner.getConnectionUrls(); - report("Connection Urls"); + out("Connection Urls"); for (String url : urls) { - report(" ", url); + out(" ", url); } @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") @@ -82,11 +82,11 @@ public static void main(String[] args) throws Exception { } } if (changed) { - report("HealthZ"); + out("HealthZ"); for (int i = 0; i < monitorPorts.length; i++) { int port = ports[i]; int mport = monitorPorts[i]; - report(" ", port + "/" + mport, hzs[i]); + out(" ", port + "/" + mport, hzs[i]); } } } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosPrinter.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosPrinter.java new file mode 100644 index 0000000..e0c0a6f --- /dev/null +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosPrinter.java @@ -0,0 +1,9 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.chaos; + +public interface ChaosPrinter { + void out(Object... objects); + void err(Object... objects); +} diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index 042945f..cc6eeab 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -19,10 +19,15 @@ import java.util.logging.Level; import static io.nats.NatsRunnerUtils.*; -import static io.synadia.chaos.ChaosUtils.report; +import static io.synadia.chaos.ChaosUtils.getDefaultPrinter; public class ChaosRunner { + private static final String CR_LABEL = "ChaosRunner"; + + private static ChaosRunner INSTANCE; + + public final ChaosPrinter printer; public final int servers; public final String clusterName; public final String serverNamePrefix; @@ -41,18 +46,6 @@ public class ChaosRunner { private final ScheduledThreadPoolExecutor executor; private int downIx = 0; - public void shutdown() { - try { - for (NatsServerRunner runner : natsServerRunners ) { - try { - runner.close(); - } - catch (Exception ignore) {} - } - } - catch (Exception ignore) {} - } - public int[] getConnectionPorts() { int[] ports = new int[servers]; for (int ix = 0; ix < servers; ix++) { @@ -113,26 +106,26 @@ private void downTask() { } NatsServerRunner runner = natsServerRunners.remove(downIx); - report("DOWN", runner.getPort()); + printer.out(CR_LABEL, "DOWN", runner.getPort()); clusterInserts.add(clusterInserts.remove(downIx)); runner.close(); scheduleUp(); } catch (Throwable e) { - report("DOWN/EX", e); + printer.out(CR_LABEL, "DOWN/EX", e); } } private void upTask() { try { NatsServerRunner runner = createRunner(servers - 1); - report("UP", runner.getPort()); + printer.out(CR_LABEL, "UP", runner.getPort()); natsServerRunners.add(runner); scheduleDown(delay); } catch (Throwable e) { - report("UP/EX: ", e); + printer.out(CR_LABEL, "UP/EX: ", e); scheduleUp(); } } @@ -164,9 +157,7 @@ public String toString() { return ChaosUtils.toString(this, System.lineSeparator(), "", " ", ""); } - private static ChaosRunner INSTANCE; - - private ChaosRunner(ChaosArguments a) throws IOException { + private ChaosRunner(ChaosArguments a, ChaosPrinter printer) throws IOException { if (a.workDirectory == null) { a.workDirectory = getTemporaryJetStreamStoreDirBase(); } @@ -178,6 +169,7 @@ else if (!a.workDirectory.toFile().exists()) { throw new IllegalArgumentException("Number of servers must be 1, 3 or 5"); } + this.printer = printer; this.servers = a.servers; this.clusterName = a.clusterName; this.serverNamePrefix = a.serverNamePrefix; @@ -257,31 +249,42 @@ public static void main(String[] args) { } public static ChaosRunner start(ChaosArguments a) { - return start(a, true); + return start(a, null); } - public static ChaosRunner start(ChaosArguments a, boolean print) { + public static ChaosRunner start(ChaosArguments a, ChaosPrinter printer) { NatsServerRunner.setDefaultOutputLevel(Level.SEVERE); + final ChaosPrinter finalPrinter = printer == null ? getDefaultPrinter() : printer; + try { - INSTANCE = new ChaosRunner(a); + INSTANCE = new ChaosRunner(a, finalPrinter); } catch (IOException e) { - System.out.println("Failed to start ChaosRunner: " + e.getMessage()); + finalPrinter.err(CR_LABEL, "Failed to start ChaosRunner", e); System.exit(-1); } - if (print) { - System.out.println(ChaosUtils.toString(INSTANCE, System.lineSeparator(), "[" + System.currentTimeMillis() + "] ", " ", "")); - } Runtime.getRuntime().addShutdownHook( new Thread("app-shutdown-hook") { @Override public void run() { INSTANCE.shutdown(); - report("EXIT Chaos Runner"); + finalPrinter.out(CR_LABEL, "EXIT"); } }); return INSTANCE; } + + private void shutdown() { + try { + for (NatsServerRunner runner : natsServerRunners ) { + try { + runner.close(); + } + catch (Exception ignore) {} + } + } + catch (Exception ignore) {} + } } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java index 95fd993..88ccb36 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosUtils.java @@ -3,6 +3,9 @@ package io.synadia.chaos; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + public class ChaosUtils { public static String toString(ChaosRunner r) { @@ -50,15 +53,45 @@ private static StringBuilder stringify(int[] ints) { return sb; } - public static void report(String label, Object... parts) { - String prefix = "[" + System.currentTimeMillis() + "] " + label; - StringBuilder sb = new StringBuilder(prefix); - for (Object part : parts) { - if (part != null) { - sb.append(" | "); - sb.append(part); - } + static ChaosPrinter PRINTER; + public static ChaosPrinter getDefaultPrinter() { + if (PRINTER == null) { + PRINTER = new ChaosPrinter() { + @Override + public void out(Object... objects) { + if (objects != null && objects.length > 0) { + System.out.println(join(objects)); + } + } + + @Override + public void err(Object... objects) { + if (objects != null && objects.length > 0) { + System.err.println(join(objects)); + } + } + + private String join(Object[] objects) { + StringBuilder sb = new StringBuilder(TIME_FORMATTER.format(ZonedDateTime.now())); + for (Object object : objects) { + sb.append(" | "); + sb.append(object); + } + return sb.toString(); + } + }; } - System.out.println(sb); + return PRINTER; + } + + public static final DateTimeFormatter TIME_FORMATTER + = DateTimeFormatter.ofPattern("HH:mm:ss.SSS"); + + public static void out(Object... objects) { + getDefaultPrinter().out(objects); + } + + public static void err(Object... objects) { + getDefaultPrinter().err(objects); } } From 4325ac9009bcb43a42f995740004ab03caa84321 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 21 Aug 2025 16:23:34 -0400 Subject: [PATCH 016/135] KV Encoding Improvements * default encoders * Encoder supporting encode value only * Encoder supporting encode key only --- .../java/io/synadia/ekv/EncodedKeyResult.java | 2 +- ...dedKeyEncodedValue.java => KvEncoded.java} | 8 +- .../java/io/synadia/ekv/KvEncodedKey.java | 26 +++ ...yEncodedValue.java => KvEncodedValue.java} | 8 +- .../io/synadia/ekv/codec/ByteValueCodec.java | 17 ++ .../test/java/io/synadia/ekv/CodecTests.java | 22 ++- .../test/java/io/synadia/ekv/MiscTests.java | 42 +++++ .../java/io/synadia/ekv/WorkflowTests.java | 171 ++++++++++++++---- .../ekv/codec/GeneralByteValueCodec.java | 41 ++--- 9 files changed, 267 insertions(+), 70 deletions(-) rename encoded-kv/src/main/java/io/synadia/ekv/{KvEncodedKeyEncodedValue.java => KvEncoded.java} (94%) create mode 100644 encoded-kv/src/main/java/io/synadia/ekv/KvEncodedKey.java rename encoded-kv/src/main/java/io/synadia/ekv/{KvStringKeyEncodedValue.java => KvEncodedValue.java} (55%) create mode 100644 encoded-kv/src/main/java/io/synadia/ekv/codec/ByteValueCodec.java create mode 100644 encoded-kv/src/test/java/io/synadia/ekv/MiscTests.java diff --git a/encoded-kv/src/main/java/io/synadia/ekv/EncodedKeyResult.java b/encoded-kv/src/main/java/io/synadia/ekv/EncodedKeyResult.java index 6f994b7..66812a2 100644 --- a/encoded-kv/src/main/java/io/synadia/ekv/EncodedKeyResult.java +++ b/encoded-kv/src/main/java/io/synadia/ekv/EncodedKeyResult.java @@ -16,7 +16,7 @@ public EncodedKeyResult(KeyResult keyResult, KeyCodec keyCodec) { this.keyCodec = keyCodec; } - public KeyType getKey() throws Exception { + public KeyType getKey() { String key = keyResult.getKey(); return key == null ? null : keyCodec.decode(key); } diff --git a/encoded-kv/src/main/java/io/synadia/ekv/KvEncodedKeyEncodedValue.java b/encoded-kv/src/main/java/io/synadia/ekv/KvEncoded.java similarity index 94% rename from encoded-kv/src/main/java/io/synadia/ekv/KvEncodedKeyEncodedValue.java rename to encoded-kv/src/main/java/io/synadia/ekv/KvEncoded.java index 93fadec..ac2959b 100644 --- a/encoded-kv/src/main/java/io/synadia/ekv/KvEncodedKeyEncodedValue.java +++ b/encoded-kv/src/main/java/io/synadia/ekv/KvEncoded.java @@ -18,22 +18,22 @@ import java.util.ArrayList; import java.util.List; -public class KvEncodedKeyEncodedValue { +public class KvEncoded { private final KeyValue kv; private final KeyCodec keyCodec; private final ValueCodec valueCodec; - public KvEncodedKeyEncodedValue(Connection connection, String bucketName, KeyCodec keyCodec, ValueCodec valueCodec) throws IOException { + public KvEncoded(Connection connection, String bucketName, KeyCodec keyCodec, ValueCodec valueCodec) throws IOException { this(connection, bucketName, keyCodec, valueCodec, null); } - public KvEncodedKeyEncodedValue(Connection connection, String bucketName, KeyCodec keyCodec, ValueCodec valueCodec, KeyValueOptions kvo) throws IOException { + public KvEncoded(Connection connection, String bucketName, KeyCodec keyCodec, ValueCodec valueCodec, KeyValueOptions kvo) throws IOException { kv = connection.keyValue(bucketName, kvo); this.keyCodec = keyCodec; this.valueCodec = valueCodec; } - public KvEncodedKeyEncodedValue(KeyValue kv, KeyCodec keyCodec, ValueCodec valueCodec) { + public KvEncoded(KeyValue kv, KeyCodec keyCodec, ValueCodec valueCodec) { this.kv = kv; this.keyCodec = keyCodec; this.valueCodec = valueCodec; diff --git a/encoded-kv/src/main/java/io/synadia/ekv/KvEncodedKey.java b/encoded-kv/src/main/java/io/synadia/ekv/KvEncodedKey.java new file mode 100644 index 0000000..d217cb1 --- /dev/null +++ b/encoded-kv/src/main/java/io/synadia/ekv/KvEncodedKey.java @@ -0,0 +1,26 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.ekv; + +import io.nats.client.Connection; +import io.nats.client.KeyValue; +import io.nats.client.KeyValueOptions; +import io.synadia.ekv.codec.ByteValueCodec; +import io.synadia.ekv.codec.KeyCodec; + +import java.io.IOException; + +public class KvEncodedKey extends KvEncoded { + public KvEncodedKey(Connection connection, String bucketName, KeyCodec keyCodec) throws IOException { + super(connection, bucketName, keyCodec, new ByteValueCodec()); + } + + public KvEncodedKey(Connection connection, String bucketName, KeyCodec keyCodec, KeyValueOptions kvo) throws IOException { + super(connection, bucketName, keyCodec, new ByteValueCodec(), kvo); + } + + public KvEncodedKey(KeyValue kv, KeyCodec keyCodec) { + super(kv, keyCodec, new ByteValueCodec()); + } +} diff --git a/encoded-kv/src/main/java/io/synadia/ekv/KvStringKeyEncodedValue.java b/encoded-kv/src/main/java/io/synadia/ekv/KvEncodedValue.java similarity index 55% rename from encoded-kv/src/main/java/io/synadia/ekv/KvStringKeyEncodedValue.java rename to encoded-kv/src/main/java/io/synadia/ekv/KvEncodedValue.java index e79c9f9..6ec96af 100644 --- a/encoded-kv/src/main/java/io/synadia/ekv/KvStringKeyEncodedValue.java +++ b/encoded-kv/src/main/java/io/synadia/ekv/KvEncodedValue.java @@ -11,16 +11,16 @@ import java.io.IOException; -public class KvStringKeyEncodedValue extends KvEncodedKeyEncodedValue { - public KvStringKeyEncodedValue(Connection connection, String bucketName, ValueCodec valueCodec) throws IOException { +public class KvEncodedValue extends KvEncoded { + public KvEncodedValue(Connection connection, String bucketName, ValueCodec valueCodec) throws IOException { super(connection, bucketName, new StringKeyCodec(), valueCodec); } - public KvStringKeyEncodedValue(Connection connection, String bucketName, ValueCodec valueCodec, KeyValueOptions kvo) throws IOException { + public KvEncodedValue(Connection connection, String bucketName, ValueCodec valueCodec, KeyValueOptions kvo) throws IOException { super(connection, bucketName, new StringKeyCodec(), valueCodec, kvo); } - public KvStringKeyEncodedValue(KeyValue kv, ValueCodec valueCodec) { + public KvEncodedValue(KeyValue kv, ValueCodec valueCodec) { super(kv, new StringKeyCodec(), valueCodec); } } diff --git a/encoded-kv/src/main/java/io/synadia/ekv/codec/ByteValueCodec.java b/encoded-kv/src/main/java/io/synadia/ekv/codec/ByteValueCodec.java new file mode 100644 index 0000000..6c74c74 --- /dev/null +++ b/encoded-kv/src/main/java/io/synadia/ekv/codec/ByteValueCodec.java @@ -0,0 +1,17 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.ekv.codec; + +public class ByteValueCodec implements ValueCodec { + + @Override + public byte[] encode(byte[] value) { + return value; + } + + @Override + public byte[] decode(byte[] encodedValue) { + return encodedValue; + } +} diff --git a/encoded-kv/src/test/java/io/synadia/ekv/CodecTests.java b/encoded-kv/src/test/java/io/synadia/ekv/CodecTests.java index cb85e32..69e08d8 100644 --- a/encoded-kv/src/test/java/io/synadia/ekv/CodecTests.java +++ b/encoded-kv/src/test/java/io/synadia/ekv/CodecTests.java @@ -3,10 +3,7 @@ package io.synadia.ekv; -import io.synadia.ekv.codec.DataValueCodec; -import io.synadia.ekv.codec.GeneralStringKeyCodec; -import io.synadia.ekv.codec.PathKeyCodec; -import io.synadia.ekv.codec.StringKeyCodec; +import io.synadia.ekv.codec.*; import io.synadia.ekv.misc.Data; import io.synadia.ekv.misc.GeneralType; import nats.io.NatsServerRunner; @@ -140,4 +137,21 @@ private void validatePathCodec(String path, String encoded, String decoded, bool assertEquals(path, pkcTrailingPath.decode(decoded + PathKeyCodec.TRAILING_SUFFIX)); } } + + @Test + public void testByteValueCodec() { + byte[] data = "data".getBytes(); + ByteValueCodec bvc = new ByteValueCodec(); + byte[] bytes = bvc.encode(data); + assertArrayEquals(data, bytes); + bytes = bvc.decode(data); + assertArrayEquals(data, bytes); + } + + @Test + public void testFilteringNotSupported() { + DataKeyCodec dkc = new DataKeyCodec(); + assertFalse(dkc.allowsFiltering()); + assertThrows(UnsupportedOperationException.class, () -> dkc.encodeFilter(null)); + } } diff --git a/encoded-kv/src/test/java/io/synadia/ekv/MiscTests.java b/encoded-kv/src/test/java/io/synadia/ekv/MiscTests.java new file mode 100644 index 0000000..cb76651 --- /dev/null +++ b/encoded-kv/src/test/java/io/synadia/ekv/MiscTests.java @@ -0,0 +1,42 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.ekv; + +import io.nats.client.api.KeyResult; +import io.synadia.ekv.codec.StringKeyCodec; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class MiscTests { + + @Test + public void testEncodedKeyResult() { + KeyResult kr = new KeyResult(); + EncodedKeyResult ekr = new EncodedKeyResult<>(kr, new StringKeyCodec()); + assertNull(ekr.getKey()); + assertNull(ekr.getException()); + assertFalse(ekr.isKey()); + assertFalse(ekr.isException()); + assertTrue(ekr.isDone()); + + kr = new KeyResult("key"); + ekr = new EncodedKeyResult<>(kr, new StringKeyCodec()); + assertNotNull(ekr.getKey()); + assertEquals(kr.getKey(), ekr.getKey()); + assertNull(ekr.getException()); + assertTrue(ekr.isKey()); + assertFalse(ekr.isException()); + assertFalse(ekr.isDone()); + + kr = new KeyResult(new Exception("message")); + ekr = new EncodedKeyResult<>(kr, new StringKeyCodec()); + assertNull(ekr.getKey()); + assertNotNull(ekr.getException()); + assertTrue(ekr.getException().getMessage().contains("message")); + assertFalse(ekr.isKey()); + assertTrue(ekr.isException()); + assertTrue(ekr.isDone()); + } +} diff --git a/encoded-kv/src/test/java/io/synadia/ekv/WorkflowTests.java b/encoded-kv/src/test/java/io/synadia/ekv/WorkflowTests.java index e83be4c..6c812f6 100644 --- a/encoded-kv/src/test/java/io/synadia/ekv/WorkflowTests.java +++ b/encoded-kv/src/test/java/io/synadia/ekv/WorkflowTests.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EnumSource; import java.nio.charset.StandardCharsets; @@ -41,27 +42,48 @@ public static void beforeAll() { } @ParameterizedTest - @EnumSource(GeneralType.class) - public void testStringKeyWorkflow(GeneralType gt) throws Exception { + @CsvSource({"PLAIN,N/A", "PLAIN,EVC", "PLAIN,EVC-NC", "PLAIN,EVC-KVO", "BASE64,N/A", "HEX,N/A"}) + public void testStringKeyWorkflow(String gtName, String variant) throws Exception { + GeneralType gt = gtName.equals("PLAIN") ? GeneralType.PLAIN : (gtName.equals("BASE64") ? GeneralType.BASE64 : GeneralType.HEX); try (NatsServerRunner runner = new NatsServerRunner(false, true)) { try (Connection nc = Nats.connect(runner.getURI())) { GeneralStringKeyCodec keyCodec = new GeneralStringKeyCodec(gt); DataValueCodec dvc = new DataValueCodec(gt); - String bucketName = NUID.nextGlobalSequence(); + String bucket = NUID.nextGlobalSequence(); KeyValueManagement kvm = nc.keyValueManagement(); - kvm.create(KeyValueConfiguration.builder().name(bucketName).build()); + kvm.create(KeyValueConfiguration.builder().name(bucket) + .maxHistoryPerKey(3) + .build()); - // this is just for coverage of constructors. - KvEncodedKeyEncodedValue ekv; + // this is just for coverage of constructors and KvEncoded classes + KvEncoded ekv; if (gt == GeneralType.PLAIN) { - KeyValue kv = nc.keyValue(bucketName); - ekv = new KvEncodedKeyEncodedValue<>(kv, keyCodec, dvc); + switch (variant) { + case "EVC": + KeyValue kv = nc.keyValue(bucket); + ekv = new KvEncodedValue<>(kv, dvc); + break; + case "EVC-NC": + ekv = new KvEncodedValue<>(nc, bucket, dvc); + break; + case "EVC-KVO": + ekv = new KvEncodedValue<>(nc, bucket, dvc, null); // COVERAGE + break; + default: + ekv = new KvEncoded<>(nc, bucket, keyCodec, dvc); + } + } + else if (gt == GeneralType.BASE64) { + KeyValue kv = nc.keyValue(bucket); + ekv = new KvEncoded<>(kv, keyCodec, dvc); } else { - ekv = new KvEncodedKeyEncodedValue<>(nc, bucketName, keyCodec, dvc); + ekv = new KvEncoded<>(nc, bucket, keyCodec, dvc); } + assertEquals(bucket, ekv.getBucketName()); + String key1 = "key.1"; String key2 = "key.2"; List keyList = new ArrayList<>(); @@ -71,12 +93,16 @@ public void testStringKeyWorkflow(GeneralType gt) throws Exception { Data v1 = new Data("v1", "foo", false); Data v2 = new Data("v2", "bar", false); - validatePutRevision(1, ekv.put(key1, v1)); - validatePutRevision(2, ekv.put(key2, v2)); + validateRevision(1, ekv.put(key1, v1)); + validateRevision(2, ekv.put(key2, v2)); - validateGet(key1, v1, ekv.get(key1)); - validateGet(key2, v2, ekv.get(key2)); + validateGet(bucket, key1, v1, ekv.get(key1)); + validateGet(bucket, key1, v1, ekv.get(key1, 1)); // COVERAGE for revision + validateGet(bucket, key2, v2, ekv.get(key2)); + validateGet(bucket, key2, v2, ekv.get(key2, 2)); // COVERAGE for revision + assertNull(ekv.get(key1, 99)); + assertNull(ekv.get(key2, 99)); assertNull(ekv.get("not-found")); validateKeys(key1, key2, ekv.keys()); @@ -91,23 +117,23 @@ public void testStringKeyWorkflow(GeneralType gt) throws Exception { validateKeys(null, key2, getFromQueue(ekv.consumeKeys(key2))); validateKeys(key1, key2, getFromQueue(ekv.consumeKeys(keyList))); - String stream = "KV_" + bucketName; + String stream = "KV_" + bucket; JetStreamSubscription sub = nc.jetStream().subscribe(">", PushSubscribeOptions.builder().stream(stream).build()); Message m1 = sub.nextMessage(Duration.ofSeconds(1)); Message m2 = sub.nextMessage(Duration.ofSeconds(1)); switch (gt) { case PLAIN: - assertEquals("$KV." + bucketName + ".key.1", m1.getSubject()); - assertEquals("$KV." + bucketName + ".key.2", m2.getSubject()); + assertEquals("$KV." + bucket + ".key.1", m1.getSubject()); + assertEquals("$KV." + bucket + ".key.2", m2.getSubject()); assertArrayEquals(v1.serialize(), m1.getData()); assertArrayEquals(v2.serialize(), m2.getData()); break; case BASE64: String encKey1 = keyCodec.encode(key1); String encKey2 = keyCodec.encode(key2); - assertEquals("$KV." + bucketName + "." + encKey1, m1.getSubject()); - assertEquals("$KV." + bucketName + "." + encKey2, m2.getSubject()); + assertEquals("$KV." + bucket + "." + encKey1, m1.getSubject()); + assertEquals("$KV." + bucket + "." + encKey2, m2.getSubject()); Base64 base64 = new Base64(); assertArrayEquals(base64.encode(v1.serialize()), m1.getData()); assertArrayEquals(base64.encode(v2.serialize()), m2.getData()); @@ -115,14 +141,87 @@ public void testStringKeyWorkflow(GeneralType gt) throws Exception { case HEX: String encKeyH1 = keyCodec.encode(key1); String encKeyH2 = keyCodec.encode(key2); - assertEquals("$KV." + bucketName + "." + encKeyH1, m1.getSubject()); - assertEquals("$KV." + bucketName + "." + encKeyH2, m2.getSubject()); + assertEquals("$KV." + bucket + "." + encKeyH1, m1.getSubject()); + assertEquals("$KV." + bucket + "." + encKeyH2, m2.getSubject()); Hex hex = new Hex(); assertArrayEquals(hex.encode(v1.serialize()), m1.getData()); assertArrayEquals(hex.encode(v2.serialize()), m2.getData()); } + + String key3 = "key.3"; + Data v3a = new Data("v3", "aaa", false); + Data v3b = new Data("v3", "bbb", false); + Data v3c = new Data("v3", "ccc", false); + Data v3d = new Data("v3", "ddd", false); + validateRevision(3, ekv.create(key3, v3a)); + validateGet(bucket, key3, v3a, ekv.get(key3)); + + List dataHistory = new ArrayList<>(); + dataHistory.add(v3a); + assertHistory(dataHistory, ekv.history(key3)); + + assertThrows(JetStreamApiException.class, () -> ekv.create(key3, v3a)); + + validateRevision(4, ekv.update(key3, v3b, 3)); + validateGet(bucket, key3, v3b, ekv.get(key3)); + dataHistory.add(v3b); + assertHistory(dataHistory, ekv.history(key3)); + + assertThrows(JetStreamApiException.class, () -> ekv.delete(key3, 3)); // COVERAGE + assertThrows(JetStreamApiException.class, () -> ekv.purge(key3, 3)); // COVERAGE + + ekv.delete(key3); + assertNull(ekv.get(key3)); + + dataHistory.add(KeyValueOperation.DELETE); + assertHistory(dataHistory, ekv.history(key3)); + + // revision is 6 b/c delete + validateRevision(6, ekv.put(key3, v3c)); + validateGet(bucket, key3, v3c, ekv.get(key3)); + + dataHistory.clear(); + dataHistory.add(v3b); + dataHistory.add(KeyValueOperation.DELETE); + dataHistory.add(v3c); + assertHistory(dataHistory, ekv.history(key3)); + + ekv.delete(key3, 6); + assertNull(ekv.get(key3)); + dataHistory.clear(); + dataHistory.add(KeyValueOperation.DELETE); + dataHistory.add(v3c); + dataHistory.add(KeyValueOperation.DELETE); + assertHistory(dataHistory, ekv.history(key3)); + + // revision is 8 b/c delete + validateRevision(8, ekv.put(key3, v3d)); + validateGet(bucket, key3, v3d, ekv.get(key3)); + dataHistory.remove(0); // delete is replaced + dataHistory.add(v3d); + + assertHistory(dataHistory, ekv.history(key3)); + ekv.purge(key3, 8); + assertNull(ekv.get(key3)); + dataHistory.clear(); + dataHistory.add(KeyValueOperation.PURGE); + assertHistory(dataHistory, ekv.history(key3)); + } + } + } + + private void assertHistory(List expected, List> apiHistory) { + System.out.println(); + for (int x = 0; x < apiHistory.size(); x++) { + Object o = expected.get(x); + if (o instanceof KeyValueOperation) { + assertEquals(o, apiHistory.get(x).getOperation()); + } + else { + assertEquals(o, apiHistory.get(x).getValue()); } } + assertEquals(apiHistory.size(), expected.size()); } @Test @@ -132,23 +231,27 @@ public void testKeyWorkflow() throws Exception { DataKeyCodec dkc = new DataKeyCodec(); DataValueCodec dvc = new DataValueCodec(GeneralType.BASE64); - String bucketName = NUID.nextGlobalSequence(); + String bucket = NUID.nextGlobalSequence(); KeyValueManagement kvm = nc.keyValueManagement(); - kvm.create(KeyValueConfiguration.builder().name(bucketName).build()); + kvm.create(KeyValueConfiguration.builder().name(bucket).build()); - KvEncodedKeyEncodedValue ekv = new KvEncodedKeyEncodedValue<>(nc, bucketName, dkc, dvc); + KvEncoded ekv = new KvEncoded<>(nc, bucket, dkc, dvc); Data key1 = new Data("foo1", null, true); Data key2 = new Data("foo2", null, true); Data v1 = new Data("bar1", "baz1", false); Data v2 = new Data("bar2", "baz2", false); - validatePutRevision(1, ekv.put(key1, v1)); - validatePutRevision(2, ekv.put(key2, v2)); + validateRevision(1, ekv.put(key1, v1)); + validateRevision(2, ekv.put(key2, v2)); - validateGet(key1, v1, ekv.get(key1)); - validateGet(key2, v2, ekv.get(key2)); + validateGet(bucket, key1, v1, ekv.get(key1)); + validateGet(bucket, key1, v1, ekv.get(key1, 1)); // COVERAGE for revision + validateGet(bucket, key2, v2, ekv.get(key2)); + validateGet(bucket, key2, v2, ekv.get(key2, 2)); // COVERAGE for revision + assertNull(ekv.get(key1, 99)); + assertNull(ekv.get(key2, 99)); assertNull(ekv.get(new Data("not-found", null, true))); validateKeys(key1, key2, ekv.keys()); @@ -166,23 +269,23 @@ public void testKeyWorkflow() throws Exception { } } - private static void validatePutRevision(long expectedRev, long actualRev) { + private static void validateRevision(long expectedRev, long actualRev) { assertEquals(expectedRev, actualRev); } - private static void validateGet(T key, Data value, EncodedKeyValueEntry entry) throws Exception { + private static void validateGet(String bucket, T key, Data value, EncodedKeyValueEntry entry) { assertNotNull(entry); + assertEquals(bucket, entry.getBucket()); assertEquals(key, entry.getKey()); assertEquals(value, entry.getValue()); + assertTrue(entry.getRevision() >= 0); // COVERAGE + assertTrue(entry.getDelta() >= 0); // COVERAGE } private static void validateKeys(T key1, T key2, List keys) { int count = 0; if (key1 != null) { count++; - if (!keys.contains(key1)) { - int x = 0; - } assertTrue(keys.contains(key1)); } else { @@ -274,7 +377,7 @@ public void endOfData() { static String TEST_WATCH_KEY_2 = "key.2"; interface TestWatchSubSupplier { - NatsKeyValueWatchSubscription get(KvEncodedKeyEncodedValue kv) throws Exception; + NatsKeyValueWatchSubscription get(KvEncoded kv) throws Exception; } @ParameterizedTest @@ -388,7 +491,7 @@ private void _testWatch(Connection nc, GeneralType gt, TestKeyValueWatcher watch .storageType(StorageType.Memory) .build()); - KvEncodedKeyEncodedValue kv = new KvEncodedKeyEncodedValue<>(nc.keyValue(bucket), watcher.keyCodec, watcher.valueCodec); + KvEncoded kv = new KvEncoded<>(nc.keyValue(bucket), watcher.keyCodec, watcher.valueCodec); NatsKeyValueWatchSubscription sub = null; diff --git a/encoded-kv/src/test/java/io/synadia/ekv/codec/GeneralByteValueCodec.java b/encoded-kv/src/test/java/io/synadia/ekv/codec/GeneralByteValueCodec.java index a678f98..08431fc 100644 --- a/encoded-kv/src/test/java/io/synadia/ekv/codec/GeneralByteValueCodec.java +++ b/encoded-kv/src/test/java/io/synadia/ekv/codec/GeneralByteValueCodec.java @@ -19,36 +19,31 @@ public GeneralByteValueCodec(GeneralType gt) { @Override public byte[] encode(byte[] value) { - if (value == null) { - return null; - } - - switch (gt) { - case BASE64: - return Base64.encodeBase64(value); - case HEX: - return new String(Hex.encodeHex(value)).getBytes(StandardCharsets.US_ASCII); + if (value != null) { + switch (gt) { + case BASE64: + return Base64.encodeBase64(value); + case HEX: + return new String(Hex.encodeHex(value)).getBytes(StandardCharsets.US_ASCII); + } } return value; } @Override public byte[] decode(byte[] encodedValue) { - if (encodedValue == null || encodedValue.length == 0) { - return null; + if (encodedValue != null && encodedValue.length > 0) { + switch (gt) { + case BASE64: return Base64.decodeBase64(encodedValue); + case HEX: + try { + return Hex.decodeHex(new String(encodedValue, StandardCharsets.US_ASCII)); + } + catch (DecoderException e) { + throw new RuntimeException(e); + } + } } - - switch (gt) { - case BASE64: return Base64.decodeBase64(encodedValue); - case HEX: - try { - return Hex.decodeHex(new String(encodedValue, StandardCharsets.US_ASCII)); - } - catch (DecoderException e) { - throw new RuntimeException(e); - } - } - return encodedValue; } } From bfa6025d299a87110afef2951be4e773eb3b7e57 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 22 Aug 2025 13:31:29 -0400 Subject: [PATCH 017/135] Initial Counter implementation --- .github/workflows/counter-main.yml | 47 ++++ .github/workflows/counter-pr.yml | 41 ++++ .github/workflows/counter-release.yml | 39 ++++ .github/workflows/cr-main.yml | 4 - .github/workflows/cr-pr.yml | 4 - .github/workflows/cr-release.yml | 4 - .github/workflows/db-main.yml | 4 - .github/workflows/db-pr.yml | 4 - .github/workflows/db-release.yml | 4 - .github/workflows/ekv-main.yml | 4 - .github/workflows/ekv-pr.yml | 4 - .github/workflows/ekv-release.yml | 4 - .github/workflows/pubx-main.yml | 4 - .github/workflows/pubx-pr.yml | 4 - .github/workflows/pubx-release.yml | 4 - .github/workflows/retrier-main.yml | 4 - .github/workflows/retrier-pr.yml | 4 - .github/workflows/retrier-release.yml | 4 - .github/workflows/rm-main.yml | 4 - .github/workflows/rm-pr.yml | 4 - .github/workflows/rm-release.yml | 4 - chaos-runner/build.gradle | 2 +- counter/.gitignore | 77 +++++++ counter/LICENSE | 201 +++++++++++++++++ counter/NOTICE | 5 + counter/README.md | 20 ++ counter/build.gradle | 207 ++++++++++++++++++ counter/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + counter/gradlew | 183 ++++++++++++++++ counter/gradlew.bat | 103 +++++++++ counter/settings.gradle | 10 + .../examples/CounterContextExample.java | 114 ++++++++++ .../io/synadia/counter/CounterContext.java | 136 ++++++++++++ .../java/io/synadia/counter/CounterEntry.java | 112 ++++++++++ .../io/synadia/counter/CounterIterator.java | 87 ++++++++ .../java/io/synadia/counter/CounterUtils.java | 25 +++ counter/src/main/javadoc/images/favicon.ico | Bin 0 -> 1150 bytes .../src/main/javadoc/images/large-logo.png | Bin 0 -> 6533 bytes .../src/main/javadoc/images/synadia-logo.png | Bin 0 -> 19014 bytes counter/src/main/javadoc/overview.html | 13 ++ counter/src/main/resources/placeholder.txt | 1 + counter/src/test/resources/placeholder.txt | 1 + counter/test.bat | 5 + direct-batch/build.gradle | 2 +- .../io/synadia/examples/ExampleUtils.java | 8 +- .../io/synadia/direct/DirectBatchContext.java | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- 50 files changed, 1443 insertions(+), 81 deletions(-) create mode 100644 .github/workflows/counter-main.yml create mode 100644 .github/workflows/counter-pr.yml create mode 100644 .github/workflows/counter-release.yml create mode 100644 counter/.gitignore create mode 100644 counter/LICENSE create mode 100644 counter/NOTICE create mode 100644 counter/README.md create mode 100644 counter/build.gradle create mode 100644 counter/gradle/wrapper/gradle-wrapper.jar create mode 100644 counter/gradle/wrapper/gradle-wrapper.properties create mode 100644 counter/gradlew create mode 100644 counter/gradlew.bat create mode 100644 counter/settings.gradle create mode 100644 counter/src/examples/java/io/synadia/examples/CounterContextExample.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterContext.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterEntry.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterIterator.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterUtils.java create mode 100644 counter/src/main/javadoc/images/favicon.ico create mode 100644 counter/src/main/javadoc/images/large-logo.png create mode 100644 counter/src/main/javadoc/images/synadia-logo.png create mode 100644 counter/src/main/javadoc/overview.html create mode 100644 counter/src/main/resources/placeholder.txt create mode 100644 counter/src/test/resources/placeholder.txt create mode 100644 counter/test.bat diff --git a/.github/workflows/counter-main.yml b/.github/workflows/counter-main.yml new file mode 100644 index 0000000..02a7f0d --- /dev/null +++ b/.github/workflows/counter-main.yml @@ -0,0 +1,47 @@ +name: Counter Main Snapshot + +on: + push: + branches: + - main + paths: + - 'counter/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd counter + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd counter + ./gradlew javadoc + popd + - name: Publish Snapshot + run: | + pushd counter + ./gradlew -i publishToSonatype + popd diff --git a/.github/workflows/counter-pr.yml b/.github/workflows/counter-pr.yml new file mode 100644 index 0000000..bc2c9f1 --- /dev/null +++ b/.github/workflows/counter-pr.yml @@ -0,0 +1,41 @@ +name: Counter Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'counter/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd counter + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd counter + ./gradlew javadoc + popd diff --git a/.github/workflows/counter-release.yml b/.github/workflows/counter-release.yml new file mode 100644 index 0000000..912c303 --- /dev/null +++ b/.github/workflows/counter-release.yml @@ -0,0 +1,39 @@ +name: Counter Release + +on: + push: + tags: [ 'counter/*' ] + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: "release" + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd counter + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify, Sign and Publish Release + run: | + pushd counter + ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + popd diff --git a/.github/workflows/cr-main.yml b/.github/workflows/cr-main.yml index a162f89..babbd7f 100644 --- a/.github/workflows/cr-main.yml +++ b/.github/workflows/cr-main.yml @@ -7,9 +7,6 @@ on: paths: - 'chaos-runner/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/cr-pr.yml b/.github/workflows/cr-pr.yml index 337c884..9d59f24 100644 --- a/.github/workflows/cr-pr.yml +++ b/.github/workflows/cr-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'chaos-runner/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/cr-release.yml b/.github/workflows/cr-release.yml index 47f7cdd..a01b18a 100644 --- a/.github/workflows/cr-release.yml +++ b/.github/workflows/cr-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'cr/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/db-main.yml b/.github/workflows/db-main.yml index 690cbd0..8ba10a6 100644 --- a/.github/workflows/db-main.yml +++ b/.github/workflows/db-main.yml @@ -7,9 +7,6 @@ on: paths: - 'direct-batch/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/db-pr.yml b/.github/workflows/db-pr.yml index 19c54c8..92f1b62 100644 --- a/.github/workflows/db-pr.yml +++ b/.github/workflows/db-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'direct-batch/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/db-release.yml b/.github/workflows/db-release.yml index 0d2fb92..b1db120 100644 --- a/.github/workflows/db-release.yml +++ b/.github/workflows/db-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'db/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/ekv-main.yml b/.github/workflows/ekv-main.yml index 26b6c59..30c4c98 100644 --- a/.github/workflows/ekv-main.yml +++ b/.github/workflows/ekv-main.yml @@ -7,9 +7,6 @@ on: paths: - 'encoded-kv/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/ekv-pr.yml b/.github/workflows/ekv-pr.yml index 15e0e9b..9b87465 100644 --- a/.github/workflows/ekv-pr.yml +++ b/.github/workflows/ekv-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'encoded-kv/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/ekv-release.yml b/.github/workflows/ekv-release.yml index 699bee7..2cc62dc 100644 --- a/.github/workflows/ekv-release.yml +++ b/.github/workflows/ekv-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'ekv/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/pubx-main.yml b/.github/workflows/pubx-main.yml index 4de4f09..dfe93d7 100644 --- a/.github/workflows/pubx-main.yml +++ b/.github/workflows/pubx-main.yml @@ -7,9 +7,6 @@ on: paths: - 'js-publish-extensions/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/pubx-pr.yml b/.github/workflows/pubx-pr.yml index d04ccf0..b9c9a82 100644 --- a/.github/workflows/pubx-pr.yml +++ b/.github/workflows/pubx-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'js-publish-extensions/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/pubx-release.yml b/.github/workflows/pubx-release.yml index 8f5c08e..efdf855 100644 --- a/.github/workflows/pubx-release.yml +++ b/.github/workflows/pubx-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'pubx/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/retrier-main.yml b/.github/workflows/retrier-main.yml index ef3df39..7d51520 100644 --- a/.github/workflows/retrier-main.yml +++ b/.github/workflows/retrier-main.yml @@ -7,9 +7,6 @@ on: paths: - 'retrier/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/retrier-pr.yml b/.github/workflows/retrier-pr.yml index 79b891f..31beed7 100644 --- a/.github/workflows/retrier-pr.yml +++ b/.github/workflows/retrier-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'retrier/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/retrier-release.yml b/.github/workflows/retrier-release.yml index 42a54ee..853e04c 100644 --- a/.github/workflows/retrier-release.yml +++ b/.github/workflows/retrier-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'retrier/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/rm-main.yml b/.github/workflows/rm-main.yml index 7d1b0b8..c9e62ea 100644 --- a/.github/workflows/rm-main.yml +++ b/.github/workflows/rm-main.yml @@ -7,9 +7,6 @@ on: paths: - 'request-many/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/rm-pr.yml b/.github/workflows/rm-pr.yml index 8527a70..5938636 100644 --- a/.github/workflows/rm-pr.yml +++ b/.github/workflows/rm-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'request-many/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/rm-release.yml b/.github/workflows/rm-release.yml index e6ab31c..d186d36 100644 --- a/.github/workflows/rm-release.yml +++ b/.github/workflows/rm-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'rm/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 9a93d45..a36b7a1 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -40,7 +40,7 @@ repositories { dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' - implementation 'io.nats:jnats:2.21.4' // this is only for the example and the uber jar won't include it + implementation 'io.nats:jnats:2.21.5' // this is only for the example and the uber jar won't include it testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000..b3e2ca5 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,77 @@ + +# NATS stuff # +############## +gnatsd.log +*.csv + +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +/bin + +# Packages # +############ +*.7z +*.dmg +*.gz +*.iso +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log + +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db + +# Editor Files # +################ +*~ +*.swp +.sts4-cache/* + +# Gradle Files # +################ +.gradle +.m2 + +# Build output directies +/target +*/target +/build +*/build + +# IntelliJ specific files/directories +out +.idea +*.ipr +*.iws +*.iml +atlassian-ide-plugin.xml + +# Eclipse specific files/directories +.classpath +.project +.settings +.metadata + +# NetBeans specific files/directories +.nbattrs + +# VSCode +.vscode/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/target/ diff --git a/counter/LICENSE b/counter/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/counter/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/counter/NOTICE b/counter/NOTICE new file mode 100644 index 0000000..ff3c8b4 --- /dev/null +++ b/counter/NOTICE @@ -0,0 +1,5 @@ +Orbit Java +Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. + +This product includes software developed at +Synadia Communications Inc. \ No newline at end of file diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000..fb6ac8b --- /dev/null +++ b/counter/README.md @@ -0,0 +1,20 @@ +![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) + +# JetStream Distributed Counter CRDT + +https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md + + +**Current Release**: N/A +  **Current Snapshot**: 0.1.0-SNAPSHOT +  **Gradle and Maven** `io.synadia:counter` +[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) + +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counter-00BC8E?labelColor=grey&style=flat) +[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter) +[![javadoc](https://javadoc.io/badge2/io.synadia/counter/javadoc.svg)](https://javadoc.io/doc/io.synadia/counter) + +--- +Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. diff --git a/counter/build.gradle b/counter/build.gradle new file mode 100644 index 0000000..8bbb6ff --- /dev/null +++ b/counter/build.gradle @@ -0,0 +1,207 @@ +import aQute.bnd.gradle.Bundle +import org.gradle.internal.os.OperatingSystem + +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'jacoco' + id 'com.github.kt3k.coveralls' version '2.12.0' + id 'biz.aQute.bnd.builder' version '5.1.2' + id "org.gradle.test-retry" version "1.1.9" + id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' + id 'signing' +} + +def jarVersion = "0.0.1" +group = 'io.synadia' + +def isMerge = System.getenv("BUILD_EVENT") == "push" +def isRelease = System.getenv("BUILD_EVENT") == "release" + +// version is the variable the build actually uses. +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() + maven { + url "https://repo1.maven.org/maven2/" + } + maven { + url "https://central.sonatype.com/repository/maven-snapshots/" + } +} + +dependencies { + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.synadia:direct-batch:0.1.2' + implementation 'org.jspecify:jspecify:1.0.0' + + testImplementation 'commons-codec:commons-codec:1.18.0' + testImplementation 'io.nats:jnats-server-runner:1.2.8' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' +} + +sourceSets { + main { + java { + srcDirs = ['src/main/java','src/examples/java'] + } + } + test { + java { + srcDirs = ['src/test/java'] + } + } +} + +tasks.register('bundle', Bundle) { + from sourceSets.main.output + exclude("io/synadia/examples/**") +} + +jar { + manifest { + attributes('Automatic-Module-Name': 'io.synadia.counter') + } + bnd (['Implementation-Title': 'JetStream Distributed Counter', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io'] + ) + exclude("io/synadia/examples/**") +} + +test { + // Use junit platform for unit tests + useJUnitPlatform() +} + +javadoc { + options.overview = 'src/main/javadoc/overview.html' // relative to source root + source = sourceSets.main.allJava + title = "Synadia Communications Inc. JetStream Distributed Counter" + classpath = sourceSets.main.runtimeClasspath + doLast { + if (!OperatingSystem.current().isWindows()) { + exec { + println "Updating favicon on all html files" + workingDir 'build/docs/javadoc' + // Only on linux, mac at this point + commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' + } + copy { + println "Copying images to javadoc folder" + from 'src/main/javadoc/images' + into 'build/docs/javadoc' + } + } + } +} + +tasks.register('examplesJar', Jar) { + archiveClassifier.set('examples') + manifest { + attributes('Implementation-Title': 'JetStream Distributed Counters', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io') + } + from(sourceSets.main.output) { + include "io/synadia/examples/**" + } +} + +tasks.register('javadocJar', Jar) { + archiveClassifier.set('javadoc') + from javadoc +} + +tasks.register('sourcesJar', Jar) { + archiveClassifier.set('sources') + from sourceSets.main.allSource +} + +jacoco { + toolVersion = "0.8.6" +} + +jacocoTestReport { + reports { + xml.enabled = true // coveralls plugin depends on xml format report + html.enabled = true + } + afterEvaluate { // only report on main library not examples + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, + exclude: ['**/examples**']) + })) + } +} + +artifacts { + archives javadocJar, sourcesJar, examplesJar +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username = System.getenv('OSSRH_USERNAME') + password = System.getenv('OSSRH_PASSWORD') + } + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact examplesJar + artifact javadocJar + pom { + name = 'counter' + packaging = 'jar' + groupId = group + artifactId = archivesBaseName + description = 'Synadia Communications Inc. JetStream Distributed Counters' + url = 'https://github.com/synadia-io/orbit.java' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = "synadia" + name = "Synadia" + email = "info@synadia.com" + url = "https://synadia.io" + } + } + scm { + url = 'https://github.com/synadia-io/orbit.java' + } + } + } + } +} + +if (isRelease) { + signing { + def signingKeyId = System.getenv('SIGNING_KEY_ID') + def signingKey = System.getenv('SIGNING_KEY') + def signingPassword = System.getenv('SIGNING_PASSWORD') + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + sign configurations.archives + sign publishing.publications.mavenJava + } +} \ No newline at end of file diff --git a/counter/gradle/wrapper/gradle-wrapper.jar b/counter/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 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 literal 0 HcmV?d00001 diff --git a/counter/gradle/wrapper/gradle-wrapper.properties b/counter/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..442d913 --- /dev/null +++ b/counter/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/counter/gradlew b/counter/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/counter/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/counter/gradlew.bat b/counter/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/counter/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/counter/settings.gradle b/counter/settings.gradle new file mode 100644 index 0000000..59f0439 --- /dev/null +++ b/counter/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'counter' diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java new file mode 100644 index 0000000..468ecad --- /dev/null +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -0,0 +1,114 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.Nats; +import io.nats.client.api.StreamConfiguration; +import io.synadia.counter.CounterContext; +import io.synadia.counter.CounterEntry; + +import java.math.BigInteger; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +public class CounterContextExample { + + private static final String STREAM_NAME = "counter-stream"; + private static final String STREAM_SUBJECT = "cs.*"; + private static final String SUBJECT_A = "cs.A"; + private static final String SUBJECT_B = "cs.B"; + private static final String SUBJECT_C = "cs.C"; + + public static void main(String[] args) throws Exception { + try (Connection nc = Nats.connect()) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Set up a fresh counter stream + try { jsm.deleteStream(STREAM_NAME); } catch (JetStreamApiException ignore) {} + CounterContext counter = CounterContext.createCounterStream(nc, + StreamConfiguration.builder() + .name(STREAM_NAME) + .subjects(STREAM_SUBJECT) + .build()); + + System.out.println("1: Add to a subject..."); + System.out.println("add(\"" + SUBJECT_A + "\", 1) -> " + counter.add(SUBJECT_A, 1)); + System.out.println("add(\"" + SUBJECT_A + "\", 2) -> " + counter.add(SUBJECT_A, 2)); + System.out.println("add(\"" + SUBJECT_A + "\", 3) -> " + counter.add(SUBJECT_A, 3)); + System.out.println("add(\"" + SUBJECT_A + "\", -1) -> " + counter.add(SUBJECT_A, -1)); + + System.out.println("add(\"" + SUBJECT_B + "\", 10) -> " + counter.add(SUBJECT_B, 10)); + System.out.println("add(\"" + SUBJECT_B + "\", 20) -> " + counter.add(SUBJECT_B, 20)); + System.out.println("add(\"" + SUBJECT_B + "\", 30) -> " + counter.add(SUBJECT_B, 30)); + System.out.println("add(\"" + SUBJECT_B + "\", -10) -> " + counter.add(SUBJECT_B, -10)); + + System.out.println("add(\"" + SUBJECT_C + "\", 100) -> " + counter.add(SUBJECT_C, 100)); + System.out.println("add(\"" + SUBJECT_C + "\", 200) -> " + counter.add(SUBJECT_C, 200)); + System.out.println("add(\"" + SUBJECT_C + "\", 300) -> " + counter.add(SUBJECT_C, 300)); + System.out.println("add(\"" + SUBJECT_C + "\", -100) -> " + counter.add(SUBJECT_C, -100)); + + System.out.println("\n2.1: Get the value for existing subjects"); + System.out.println("get(\"" + SUBJECT_A + "\") -> " + counter.get(SUBJECT_A)); + System.out.println("get(\"" + SUBJECT_B + "\") -> " + counter.get(SUBJECT_B)); + System.out.println("get(\"" + SUBJECT_C + "\") -> " + counter.get(SUBJECT_C)); + + System.out.println("\n2.2 Get the value if subject not found"); + try { + counter.get("cs.X"); + } + catch (JetStreamApiException e) { + System.out.println("get(\"X\") -> " + e); + } + + System.out.println("\n3: Get the full entry for a subject, notice the last increment..."); + System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n4: Get multiples entries - maybe to total them up"); + LinkedBlockingQueue q = counter.getEntries(SUBJECT_A, SUBJECT_B, SUBJECT_C); + BigInteger total = BigInteger.ZERO; + CounterEntry entry = q.poll(1, TimeUnit.SECONDS); + while (entry != null && entry.isEntry()) { + System.out.println("Entry: " + entry); + total = total.add(entry.value); + entry = q.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println("The last entry was: " + entry); + System.out.println("Entries Totaled: " + total); + + System.out.println("\n5.1: Set the value for a subject"); + System.out.println("set(\"" + SUBJECT_A + "\", 9) -> " + counter.set(SUBJECT_A, 9)); + System.out.println("set(\"" + SUBJECT_B + "\", 99) -> " + counter.set(SUBJECT_B, 99)); + System.out.println("set(\"" + SUBJECT_C + "\", 999) -> " + counter.set(SUBJECT_C, 999)); + + System.out.println("\n5.2: Get the full entry again, notice the last increment after a set..."); + System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n6.1: Zero is a shortcut to set the value for a subject to 0"); + System.out.println("zero(\"" + SUBJECT_A + "\") -> " + counter.zero(SUBJECT_A)); + System.out.println("zero(\"" + SUBJECT_B + "\") -> " + counter.zero(SUBJECT_B)); + System.out.println("zero(\"" + SUBJECT_C + "\") -> " + counter.zero(SUBJECT_C)); + + System.out.println("\n6.2: Get the full entry again, notice the last increment after a zero..."); + System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n7: Get multiples entries - but subject doesn't have any counters"); + q = counter.getEntries("not-no counters"); + entry = q.poll(1, TimeUnit.SECONDS); + while (entry != null && entry.isEntry()) { + System.out.println("Entry: " + entry); + entry = q.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println("The last entry was: " + entry); + } + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterContext.java b/counter/src/main/java/io/synadia/counter/CounterContext.java new file mode 100644 index 0000000..87c9f06 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterContext.java @@ -0,0 +1,136 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.*; +import io.nats.client.api.*; +import io.nats.client.impl.Headers; +import io.synadia.direct.DirectBatchContext; +import io.synadia.direct.MessageBatchGetRequest; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; + +import static io.synadia.counter.CounterUtils.INCREMENT_HEADER; +import static io.synadia.counter.CounterUtils.extractVal; + +public class CounterContext { + + public static CounterContext createCounterStream(Connection conn, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + return createCounterStream(conn, null, userConfig); + } + + public static CounterContext createCounterStream(Connection conn, JetStreamOptions jso, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + if (userConfig.getRetentionPolicy() != RetentionPolicy.Limits) { + throw new IllegalArgumentException("Retention Policy - Limits is the only allowed limit for counter streams."); + } + if (userConfig.getDiscardPolicy() == DiscardPolicy.New) { + throw new IllegalArgumentException("Discard Policy - New is not allowed for counter streams."); + } + StreamConfiguration config = StreamConfiguration.builder(userConfig) + .allowDirect(true) + .allowMessageCounter(true) + .build(); + + JetStreamManagement jsm = conn.jetStreamManagement(jso); + jsm.addStream(config); + + return new CounterContext(config.getName(), conn, jso, jsm); + } + + private final String streamName; + private final JetStreamManagement jsm; + private final JetStream js; + private final DirectBatchContext dbCtx; + + public CounterContext(String streamName, Connection conn) throws IOException, JetStreamApiException { + this(streamName, conn, null, null); + } + + public CounterContext(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException { + this(streamName, conn, jso, null); + } + + private CounterContext(@NonNull String streamName, @NonNull Connection conn, @Nullable JetStreamOptions jso, @Nullable JetStreamManagement jsm) throws IOException, JetStreamApiException { + this.streamName = streamName; + this.jsm = jsm == null ? conn.jetStreamManagement(jso) : jsm; + js = this.jsm.jetStream(); + dbCtx = new DirectBatchContext(conn, jso, streamName); + } + + private BigInteger _add(String subject, String sv) throws IOException, JetStreamApiException { + Headers h = new Headers(); + h.put(INCREMENT_HEADER, sv); + PublishAck pa = js.publish(subject, h, null); + String val = pa.getVal(); + if (val == null) { + throw new IOException("Publish Failed"); + } + return new BigInteger(val); + } + + public BigInteger add(String subject, int value) throws JetStreamApiException, IOException { + return _add(subject, Integer.toString(value)); + } + + public BigInteger add(String subject, long value) throws JetStreamApiException, IOException { + return _add(subject, Long.toString(value)); + } + + public BigInteger add(String subject, BigInteger value) throws JetStreamApiException, IOException { + return _add(subject, value.toString()); + } + + public BigInteger increment(String subject) throws JetStreamApiException, IOException { + return _add(subject, "1"); + } + + public BigInteger decrement(String subject) throws JetStreamApiException, IOException { + return _add(subject, "-1"); + } + + public BigInteger set(String subject, int value) throws JetStreamApiException, IOException { + return set(subject, BigInteger.valueOf(value)); + } + + public BigInteger set(String subject, long value) throws JetStreamApiException, IOException { + return set(subject, BigInteger.valueOf(value)); + } + + public BigInteger set(String subject, BigInteger value) throws JetStreamApiException, IOException { + // 100 -> 200 = add(200 - 100) | 100 -> 0 = add(0 - 100) | -100 -> 0 = add(0 - -100) | -100 -> 200 = add(200 - -100) + BigInteger bi = get(subject); + return _add(subject, value.subtract(bi).toString()); + } + + public BigInteger zero(String subject) throws JetStreamApiException, IOException { + return set(subject, BigInteger.ZERO); + } + + public BigInteger get(String subject) throws JetStreamApiException, IOException { + MessageInfo mi = jsm.getLastMessage(streamName, subject); + return new BigInteger(extractVal(mi.getData())); + } + + public CounterEntry getEntry(String subject) throws JetStreamApiException, IOException { + MessageInfo mi = jsm.getLastMessage(streamName, subject); + return new CounterEntry(mi); + } + + public LinkedBlockingQueue getEntries(String... subjects) { + return getEntries(Arrays.asList(subjects)); + } + + public LinkedBlockingQueue getEntries(List subjects) { + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); + dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterEntry(mi))); + return queue; + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counter/src/main/java/io/synadia/counter/CounterEntry.java new file mode 100644 index 0000000..a547885 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterEntry.java @@ -0,0 +1,112 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import io.nats.client.support.Status; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import static io.synadia.counter.CounterUtils.extractIncrement; +import static io.synadia.counter.CounterUtils.extractVal; + +public class CounterEntry { + public final String subject; + public final BigInteger value; + public final BigInteger lastIncrement; + public final Map sources; + public final Status status; + + CounterEntry(MessageInfo mi) { + this.status = mi.getStatus(); + this.sources = new HashMap<>(); + if (mi.isMessage()) { + this.subject = mi.getSubject(); + this.value = new BigInteger(extractVal(mi.getData())); + this.lastIncrement = new BigInteger(extractIncrement(mi.getHeaders())); + } + else { + this.subject = ""; + this.value = BigInteger.ZERO; + this.lastIncrement = BigInteger.ZERO; + } + } + + /** + * Whether this CounterEntry is a regular entry + * @return true if the CounterEntry is a regular entry + */ + public boolean isEntry() { + return status == null; + } + + /** + * Whether this CounterEntry is a status message + * @return true if this CounterEntry is a status message + */ + public boolean isStatus() { + return status != null; + } + + /** + * Whether this CounterEntry is a status message and is a direct EOB status + * @return true if this CounterEntry is a status message and is a direct EOB status + */ + public boolean isEobStatus() { + return status != null && status.isEob(); + } + + /** + * Whether this CounterEntry is a status message and is an error status + * @return true if this CounterEntry is a status message and is an error status + */ + public boolean isErrorStatus() { + return status != null && !status.isEob(); + } + + @NonNull + public String getSubject() { + return subject; + } + + @NonNull + public BigInteger getValue() { + return value; + } + + @NonNull + public BigInteger getLastIncrement() { + return lastIncrement; + } + + @NonNull + public Map getSources() { + return sources; + } + + @Nullable + public Status getStatus() { + return status; + } + + @Override + public String toString() { + if (isEntry()) { + return "CounterEntry{" + + "subject='" + subject + '\'' + + ", value=" + value + + ", lastIncrement=" + lastIncrement + + ", sources=" + sources + + '}'; + } + + return "CounterEntry{" + + "status=" + status + + '}'; + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterIterator.java b/counter/src/main/java/io/synadia/counter/CounterIterator.java new file mode 100644 index 0000000..4889c04 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterIterator.java @@ -0,0 +1,87 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import io.synadia.direct.MessageInfoHandler; + +import java.math.BigInteger; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public class CounterIterator implements Iterator, MessageInfoHandler { + private final LinkedBlockingQueue queue; + private final AtomicBoolean isFinished; + private BigInteger nextElement; + private boolean hasNextComputed; + + public CounterIterator() { + this.queue = new LinkedBlockingQueue<>(); + this.isFinished = new AtomicBoolean(false); + this.nextElement = null; + this.hasNextComputed = false; + } + + @Override + public void onMessageInfo(MessageInfo messageInfo) { +// if (endMarker.equals(value)) { +// isFinished.set(true); +// } else { +// try { +// queue.put(value); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// throw new RuntimeException("Interrupted while adding value", e); +// } +// } + } + + @Override + public boolean hasNext() { + if (!hasNextComputed) { + computeNext(); + } + return nextElement != null; + } + + @Override + public BigInteger next() { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + BigInteger result = nextElement; + hasNextComputed = false; + nextElement = null; + return result; + } + + private void computeNext() { + if (hasNextComputed) { + return; + } + + try { + // If we've already seen the end marker, no more elements + if (isFinished.get() && queue.isEmpty()) { + nextElement = null; + } else { + // Wait for next element with timeout to avoid infinite blocking + nextElement = queue.poll(100, TimeUnit.MILLISECONDS); + + // If queue is empty but not finished, keep polling + while (nextElement == null && !isFinished.get()) { + nextElement = queue.poll(100, TimeUnit.MILLISECONDS); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while waiting for next element", e); + } + + hasNextComputed = true; + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterUtils.java b/counter/src/main/java/io/synadia/counter/CounterUtils.java new file mode 100644 index 0000000..2584697 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterUtils.java @@ -0,0 +1,25 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.impl.Headers; + +public final class CounterUtils { + + public static final String INCREMENT_HEADER = "Nats-Incr"; + + public static String extractVal(byte[] data) { + String s = new String(data); + // {"val":"-123"} + // don't want to assume anything about how the json is formatted/spaced + int colonAt = s.indexOf(':'); + int numberStart = s.indexOf('"', colonAt + 1) + 1; + int lastQuote = s.lastIndexOf('"'); + return s.substring(numberStart, lastQuote).trim(); + } + + public static String extractIncrement(Headers h) { + return h == null ? "0" : h.getFirst(INCREMENT_HEADER); + } +} diff --git a/counter/src/main/javadoc/images/favicon.ico b/counter/src/main/javadoc/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9464855b4b53e64a0b464a138e21734ece9a878b GIT binary patch literal 1150 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 + + + + +JetStream Distributed Counter +

Synadia Logo

+ + + + diff --git a/counter/src/main/resources/placeholder.txt b/counter/src/main/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/counter/src/main/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/counter/src/test/resources/placeholder.txt b/counter/src/test/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/counter/src/test/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/counter/test.bat b/counter/test.bat new file mode 100644 index 0000000..22c2cd1 --- /dev/null +++ b/counter/test.bat @@ -0,0 +1,5 @@ +call gradlew clean build jacocoTestReport +taskkill /F /IM nats-server.exe +start chrome file:///C:/nats/orbit.java/counter/build/reports/jacoco/test/html/index.html +start chrome file:///C:/nats/orbit.java/counter/build/reports/tests/test/index.html + diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 6fe666d..470e189 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java b/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java index 7da03a0..ac53044 100644 --- a/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java +++ b/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java @@ -25,7 +25,8 @@ public static void printMessageInfo(MessageInfo mi, Number listId) { System.out.println("[" + listId + "] Message" + " | subject: " + mi.getSubject() + " | sequence: " + mi.getSeq() - + " | time: " + DateTimeUtils.toRfc3339(mi.getTime())); + + " | time: " + (mi.getTime() == null ? "null" : DateTimeUtils.toRfc3339(mi.getTime())) + ); } else { if (mi.isEobStatus()) { @@ -34,13 +35,14 @@ public static void printMessageInfo(MessageInfo mi, Number listId) { else if (mi.isErrorStatus()) { System.out.print("[" + listId + "] MI Error"); } - else if (mi.isErrorStatus()) { + else if (mi.isStatus()) { System.out.print("[" + listId + "] MI Status"); } System.out.println(" | isStatus? " + mi.isStatus() + " | isEobStatus? " + mi.isEobStatus() + " | isErrorStatus? " + mi.isErrorStatus() - + " | status code: " + mi.getStatus().getCode()); + + " | status code: " + (mi.getStatus() == null ? "null" : mi.getStatus().getCode()) + ); } } diff --git a/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java b/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java index bc5d0b1..2dbcebc 100644 --- a/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java +++ b/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java @@ -23,7 +23,7 @@ public class DirectBatchContext { private final Connection conn; private final JetStreamOptions jso; private final String streamName; - final Duration timeout; + private final Duration timeout; /** * Construct a DirectBatchContext instance. diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index ff76be2..10ce07a 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index 94f7aa6..f862338 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index 5d7e45b..7049817 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' From 11f9242a5f9c178cc65b4d37b6322036a9c90c32 Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 24 Aug 2025 06:53:47 -0400 Subject: [PATCH 018/135] Direct batch construction accepts stream info if caller already has it. --- direct-batch/build.gradle | 9 +++- .../io/synadia/examples/ExampleUtils.java | 4 ++ .../io/synadia/direct/DirectBatchContext.java | 16 ++++-- .../direct/MessageBatchGetRequest.java | 53 +++++++++++++------ 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 470e189..346c497 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,8 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.5' + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' @@ -46,6 +47,10 @@ dependencies { testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' } +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' +} + sourceSets { main { java { @@ -201,4 +206,4 @@ if (isRelease) { sign configurations.archives sign publishing.publications.mavenJava } -} \ No newline at end of file +} diff --git a/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java b/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java index ac53044..802de7a 100644 --- a/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java +++ b/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java @@ -2,6 +2,7 @@ import io.nats.client.NUID; import io.nats.client.api.MessageInfo; +import io.nats.client.impl.Headers; import io.nats.client.support.DateTimeUtils; import java.util.ArrayList; @@ -22,10 +23,13 @@ public static void printMessageInfo(List list) { public static void printMessageInfo(MessageInfo mi, Number listId) { if (mi.isMessage()) { + Headers h = mi.getHeaders(); + String hs = (h == null || h.isEmpty()) ? "none" : h.toString(); System.out.println("[" + listId + "] Message" + " | subject: " + mi.getSubject() + " | sequence: " + mi.getSeq() + " | time: " + (mi.getTime() == null ? "null" : DateTimeUtils.toRfc3339(mi.getTime())) + + " | headers: " + hs ); } else { diff --git a/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java b/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java index 2dbcebc..e51e34b 100644 --- a/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java +++ b/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java @@ -34,7 +34,7 @@ public class DirectBatchContext { * @throws JetStreamApiException the request had an error related to the data */ public DirectBatchContext(Connection conn, String streamName) throws IOException, JetStreamApiException { - this(conn, null, streamName); + this(conn, null, streamName, null); } /** @@ -47,6 +47,10 @@ public DirectBatchContext(Connection conn, String streamName) throws IOException * @throws JetStreamApiException the request had an error related to the data */ public DirectBatchContext(Connection conn, JetStreamOptions jso, String streamName) throws IOException, JetStreamApiException { + this(conn, jso, streamName, null); + } + + public DirectBatchContext(Connection conn, JetStreamOptions jso, String streamName, StreamInfo si) throws IOException, JetStreamApiException { validateNotNull(conn, "Connection required,"); if (!conn.getServerInfo().isNewerVersionThan("2.10.99")) { throw new IllegalArgumentException("Batch direct get not available until server version 2.11.0."); @@ -55,8 +59,14 @@ public DirectBatchContext(Connection conn, JetStreamOptions jso, String streamNa this.jso = jso == null ? DEFAULT_JS_OPTIONS : jso; JetStreamManagement jsm = conn.jetStreamManagement(this.jso); - this.streamName = required(streamName, "Stream name required,"); - StreamInfo si = jsm.getStreamInfo(streamName); + if (si == null) { + this.streamName = required(streamName, "Stream name required,"); + si = jsm.getStreamInfo(streamName); + } + else { + this.streamName = si.getConfiguration().getName(); + } + if (!si.getConfiguration().getAllowDirect()) { throw new IllegalArgumentException("Stream must have allow direct set."); } diff --git a/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java b/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java index 8f8dec8..501ae3c 100644 --- a/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java +++ b/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java @@ -2,6 +2,7 @@ import io.nats.client.support.JsonSerializable; import io.nats.client.support.Validator; +import org.jspecify.annotations.NonNull; import java.time.ZonedDateTime; import java.util.List; @@ -22,6 +23,7 @@ public class MessageBatchGetRequest implements JsonSerializable { private final List multiLastBySubjects; private final long upToSequence; private final ZonedDateTime upToTime; + private final boolean noHeaders; // batch constructor private MessageBatchGetRequest(String subject, @@ -37,8 +39,40 @@ private MessageBatchGetRequest(String subject, this.multiLastBySubjects = null; this.upToSequence = -1; this.upToTime = null; - this.minSequence = startTime == null && minSequence < 1 ? 1 : minSequence; + noHeaders = false; + } + + // multi last for constructor + private MessageBatchGetRequest(List subjects, long upToSequence, ZonedDateTime upToTime, int batch) { + if (subjects == null || subjects.isEmpty()) { + throw new IllegalArgumentException("Subjects are required."); + } + this.batch = batch; + nextBySubject = null; + this.maxBytes = -1; + this.minSequence = -1; + this.startTime = null; + this.multiLastBySubjects = subjects; + this.upToSequence = upToSequence; + this.upToTime = upToTime; + noHeaders = false; + } + + private MessageBatchGetRequest(MessageBatchGetRequest r, boolean noHeaders) { + this.batch = r.batch; + this.nextBySubject = r.nextBySubject; + this.maxBytes = r.maxBytes; + this.minSequence = r.minSequence; + this.startTime = r.startTime; + this.multiLastBySubjects = r.multiLastBySubjects; + this.upToSequence = r.upToSequence; + this.upToTime = r.upToTime; + this.noHeaders = noHeaders; + } + + public MessageBatchGetRequest noHeaders() { + return new MessageBatchGetRequest(this, true); } /** @@ -108,21 +142,6 @@ public static MessageBatchGetRequest batchBytes(String subject, int batch, int m return new MessageBatchGetRequest(subject, batch, maxBytes, -1, startTime); } - // multi last for constructor - private MessageBatchGetRequest(List subjects, long upToSequence, ZonedDateTime upToTime, int batch) { - if (subjects == null || subjects.isEmpty()) { - throw new IllegalArgumentException("Subjects are required."); - } - this.batch = batch; - nextBySubject = null; - this.maxBytes = -1; - this.minSequence = -1; - this.startTime = null; - this.multiLastBySubjects = subjects; - this.upToSequence = upToSequence; - this.upToTime = upToTime; - } - /** * Get the last messages for the subjects specified subject * @param subjects the subjects, may include wildcards. @@ -253,6 +272,7 @@ public ZonedDateTime getUpToTime() { } @Override + @NonNull public String toJson() { StringBuilder sb = beginJson(); addField(sb, BATCH, batch); @@ -263,6 +283,7 @@ public String toJson() { addStrings(sb, MULTI_LAST, multiLastBySubjects); addField(sb, UP_TO_SEQ, upToSequence); addField(sb, UP_TO_TIME, upToTime); + addFldWhenTrue(sb, NO_HDR, noHeaders); return endJson(sb).toString(); } From 8a27d2c5ffad971b389e6ea588dcc4997d2c2561 Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 24 Aug 2025 07:32:58 -0400 Subject: [PATCH 019/135] finished counter example --- counter/build.gradle | 8 +- .../examples/CounterContextExample.java | 163 +++++++++++------- .../io/synadia/counter/CounterContext.java | 50 +++++- .../java/io/synadia/counter/CounterEntry.java | 2 +- .../java/io/synadia/counter/CounterValue.java | 92 ++++++++++ 5 files changed, 242 insertions(+), 73 deletions(-) create mode 100644 counter/src/main/java/io/synadia/counter/CounterValue.java diff --git a/counter/build.gradle b/counter/build.gradle index 8bbb6ff..3d92b16 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -39,7 +39,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' - implementation 'io.synadia:direct-batch:0.1.2' + implementation 'io.synadia:direct-batch:0.1.3-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'commons-codec:commons-codec:1.18.0' @@ -49,6 +49,10 @@ dependencies { testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' } +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' +} + sourceSets { main { java { @@ -204,4 +208,4 @@ if (isRelease) { sign configurations.archives sign publishing.publications.mavenJava } -} \ No newline at end of file +} diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index 468ecad..72c8d2e 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -10,6 +10,7 @@ import io.nats.client.api.StreamConfiguration; import io.synadia.counter.CounterContext; import io.synadia.counter.CounterEntry; +import io.synadia.counter.CounterValue; import java.math.BigInteger; import java.util.concurrent.LinkedBlockingQueue; @@ -36,79 +37,117 @@ public static void main(String[] args) throws Exception { .build()); System.out.println("1: Add to a subject..."); - System.out.println("add(\"" + SUBJECT_A + "\", 1) -> " + counter.add(SUBJECT_A, 1)); - System.out.println("add(\"" + SUBJECT_A + "\", 2) -> " + counter.add(SUBJECT_A, 2)); - System.out.println("add(\"" + SUBJECT_A + "\", 3) -> " + counter.add(SUBJECT_A, 3)); - System.out.println("add(\"" + SUBJECT_A + "\", -1) -> " + counter.add(SUBJECT_A, -1)); - - System.out.println("add(\"" + SUBJECT_B + "\", 10) -> " + counter.add(SUBJECT_B, 10)); - System.out.println("add(\"" + SUBJECT_B + "\", 20) -> " + counter.add(SUBJECT_B, 20)); - System.out.println("add(\"" + SUBJECT_B + "\", 30) -> " + counter.add(SUBJECT_B, 30)); - System.out.println("add(\"" + SUBJECT_B + "\", -10) -> " + counter.add(SUBJECT_B, -10)); - - System.out.println("add(\"" + SUBJECT_C + "\", 100) -> " + counter.add(SUBJECT_C, 100)); - System.out.println("add(\"" + SUBJECT_C + "\", 200) -> " + counter.add(SUBJECT_C, 200)); - System.out.println("add(\"" + SUBJECT_C + "\", 300) -> " + counter.add(SUBJECT_C, 300)); - System.out.println("add(\"" + SUBJECT_C + "\", -100) -> " + counter.add(SUBJECT_C, -100)); - - System.out.println("\n2.1: Get the value for existing subjects"); - System.out.println("get(\"" + SUBJECT_A + "\") -> " + counter.get(SUBJECT_A)); - System.out.println("get(\"" + SUBJECT_B + "\") -> " + counter.get(SUBJECT_B)); - System.out.println("get(\"" + SUBJECT_C + "\") -> " + counter.get(SUBJECT_C)); - - System.out.println("\n2.2 Get the value if subject not found"); + System.out.println(" add(\"" + SUBJECT_A + "\", 1) -> " + counter.add(SUBJECT_A, 1)); + System.out.println(" add(\"" + SUBJECT_A + "\", 2) -> " + counter.add(SUBJECT_A, 2)); + System.out.println(" add(\"" + SUBJECT_A + "\", 3) -> " + counter.add(SUBJECT_A, 3)); + System.out.println(" add(\"" + SUBJECT_A + "\", -1) -> " + counter.add(SUBJECT_A, -1)); + + System.out.println(" add(\"" + SUBJECT_B + "\", 10) -> " + counter.add(SUBJECT_B, 10)); + System.out.println(" add(\"" + SUBJECT_B + "\", 20) -> " + counter.add(SUBJECT_B, 20)); + System.out.println(" add(\"" + SUBJECT_B + "\", 30) -> " + counter.add(SUBJECT_B, 30)); + System.out.println(" add(\"" + SUBJECT_B + "\", -10) -> " + counter.add(SUBJECT_B, -10)); + + System.out.println(" add(\"" + SUBJECT_C + "\", 100) -> " + counter.add(SUBJECT_C, 100)); + System.out.println(" add(\"" + SUBJECT_C + "\", 200) -> " + counter.add(SUBJECT_C, 200)); + System.out.println(" add(\"" + SUBJECT_C + "\", 300) -> " + counter.add(SUBJECT_C, 300)); + System.out.println(" add(\"" + SUBJECT_C + "\", -100) -> " + counter.add(SUBJECT_C, -100)); + + System.out.println("\n2.1: get() for existing subjects"); + System.out.println(" get(\"" + SUBJECT_A + "\") -> " + counter.get(SUBJECT_A)); + System.out.println(" get(\"" + SUBJECT_B + "\") -> " + counter.get(SUBJECT_B)); + System.out.println(" get(\"" + SUBJECT_C + "\") -> " + counter.get(SUBJECT_C)); + + System.out.println("\n2.2: get() when the subject is not found"); try { - counter.get("cs.X"); + counter.get("not-found"); } catch (JetStreamApiException e) { - System.out.println("get(\"X\") -> " + e); + System.out.println(" get(\"not-found\") -> " + e); } - System.out.println("\n3: Get the full entry for a subject, notice the last increment..."); - System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); - System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); - System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + System.out.println("\n3: getEntry() - The full CounterEntry for a subject, notice the last increment..."); + System.out.println(" getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); - System.out.println("\n4: Get multiples entries - maybe to total them up"); - LinkedBlockingQueue q = counter.getEntries(SUBJECT_A, SUBJECT_B, SUBJECT_C); + System.out.println("\n4.1: getValues() - Get the CounterValue object for multiple subjects. Maybe to total them up?\""); + LinkedBlockingQueue qv = counter.getValues(SUBJECT_A, SUBJECT_B, SUBJECT_C); BigInteger total = BigInteger.ZERO; - CounterEntry entry = q.poll(1, TimeUnit.SECONDS); + CounterValue value = qv.poll(1, TimeUnit.SECONDS); + while (value != null && value.isValue()) { + System.out.println(" " + value); + total = total.add(value.value); + value = qv.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" The iteration is signaled done when the CounterValue is a status: " + value); + System.out.println(" Values totaled: " + total); + + System.out.println("\n4.2: getEntries() - Get CounterEntry for multiple subjects."); + LinkedBlockingQueue qe = counter.getEntries(SUBJECT_A, SUBJECT_B, SUBJECT_C); + CounterEntry entry = qe.poll(1, TimeUnit.SECONDS); + while (entry != null && entry.isEntry()) { + System.out.println(" " + entry); + entry = qe.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" CounterEntry status 204 signals no more entries: " + entry); + + System.out.println("\n5.1: set() - Set the value for a subject"); + System.out.println(" set(\"" + SUBJECT_A + "\", 9) -> " + counter.set(SUBJECT_A, 9)); + System.out.println(" set(\"" + SUBJECT_B + "\", 99) -> " + counter.set(SUBJECT_B, 99)); + System.out.println(" set(\"" + SUBJECT_C + "\", 999) -> " + counter.set(SUBJECT_C, 999)); + + System.out.println("\n5.2: getEntry() - Get the full CounterEntry, notice the last increment after a set represents" + + "\n the difference between the entry before the set and the set value."); + System.out.println(" getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n6.1: zero() is a shortcut to set the value of a subject to 0"); + System.out.println(" zero(\"" + SUBJECT_A + "\") -> " + counter.zero(SUBJECT_A)); + System.out.println(" zero(\"" + SUBJECT_B + "\") -> " + counter.zero(SUBJECT_B)); + System.out.println(" zero(\"" + SUBJECT_C + "\") -> " + counter.zero(SUBJECT_C)); + + System.out.println("\n6.2: getEntry() - Get the full CounterEntry, notice the last increment after a zero represents" + + "\n the difference between the entry before the zero and zero."); + System.out.println(" getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n7.1: getValues() - Get multiple CounterValue - but no subjects have counters"); + qv = counter.getValues("no-counters", "also-counters"); + value = qv.poll(1, TimeUnit.SECONDS); + while (value != null && value.isValue()) { + System.out.println(" " + value); + value = qv.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" CounterValue status 204 signals no more entries: " + value); + + System.out.println("\n7.2: getEntries() - Get multiple CounterEntry - but no subjects have counters"); + qe = counter.getEntries("no-counters", "also-counters"); + entry = qe.poll(1, TimeUnit.SECONDS); while (entry != null && entry.isEntry()) { - System.out.println("Entry: " + entry); - total = total.add(entry.value); - entry = q.poll(10, TimeUnit.MILLISECONDS); + System.out.println(" " + entry); + entry = qe.poll(10, TimeUnit.MILLISECONDS); } - System.out.println("The last entry was: " + entry); - System.out.println("Entries Totaled: " + total); - - System.out.println("\n5.1: Set the value for a subject"); - System.out.println("set(\"" + SUBJECT_A + "\", 9) -> " + counter.set(SUBJECT_A, 9)); - System.out.println("set(\"" + SUBJECT_B + "\", 99) -> " + counter.set(SUBJECT_B, 99)); - System.out.println("set(\"" + SUBJECT_C + "\", 999) -> " + counter.set(SUBJECT_C, 999)); - - System.out.println("\n5.2: Get the full entry again, notice the last increment after a set..."); - System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); - System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); - System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); - - System.out.println("\n6.1: Zero is a shortcut to set the value for a subject to 0"); - System.out.println("zero(\"" + SUBJECT_A + "\") -> " + counter.zero(SUBJECT_A)); - System.out.println("zero(\"" + SUBJECT_B + "\") -> " + counter.zero(SUBJECT_B)); - System.out.println("zero(\"" + SUBJECT_C + "\") -> " + counter.zero(SUBJECT_C)); - - System.out.println("\n6.2: Get the full entry again, notice the last increment after a zero..."); - System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); - System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); - System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); - - System.out.println("\n7: Get multiples entries - but subject doesn't have any counters"); - q = counter.getEntries("not-no counters"); - entry = q.poll(1, TimeUnit.SECONDS); + System.out.println(" The only CounterEntry received was a 404: " + entry); + + System.out.println("\n8.1: getValues() - Get multiple CounterValue - some subjects have any counters"); + qv = counter.getValues("no-counters", SUBJECT_A, SUBJECT_B, SUBJECT_C); + value = qv.poll(1, TimeUnit.SECONDS); + while (value != null && value.isValue()) { + System.out.println(" " + value); + value = qv.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" CounterValue status 204 signals no more entries: " + value); + + System.out.println("\n8.2: getEntries() - Get multiple CounterEntry - some subjects have any counters"); + qe = counter.getEntries("no-counters", SUBJECT_A, SUBJECT_B, SUBJECT_C); + entry = qe.poll(1, TimeUnit.SECONDS); while (entry != null && entry.isEntry()) { - System.out.println("Entry: " + entry); - entry = q.poll(10, TimeUnit.MILLISECONDS); + System.out.println(" " + entry); + entry = qe.poll(10, TimeUnit.MILLISECONDS); } - System.out.println("The last entry was: " + entry); + System.out.println(" CounterEntry status 204 signals no more entries: " + entry); } } } diff --git a/counter/src/main/java/io/synadia/counter/CounterContext.java b/counter/src/main/java/io/synadia/counter/CounterContext.java index 87c9f06..d0ae876 100644 --- a/counter/src/main/java/io/synadia/counter/CounterContext.java +++ b/counter/src/main/java/io/synadia/counter/CounterContext.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.concurrent.LinkedBlockingQueue; +import static io.nats.client.support.Validator.required; import static io.synadia.counter.CounterUtils.INCREMENT_HEADER; import static io.synadia.counter.CounterUtils.extractVal; @@ -39,9 +40,9 @@ public static CounterContext createCounterStream(Connection conn, JetStreamOptio .build(); JetStreamManagement jsm = conn.jetStreamManagement(jso); - jsm.addStream(config); + StreamInfo si = jsm.addStream(config); - return new CounterContext(config.getName(), conn, jso, jsm); + return new CounterContext(config.getName(), conn, jso, jsm, si); } private final String streamName; @@ -50,18 +51,40 @@ public static CounterContext createCounterStream(Connection conn, JetStreamOptio private final DirectBatchContext dbCtx; public CounterContext(String streamName, Connection conn) throws IOException, JetStreamApiException { - this(streamName, conn, null, null); + this(streamName, conn, null, null, null); } public CounterContext(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException { - this(streamName, conn, jso, null); + this(streamName, conn, jso, null, null); } - private CounterContext(@NonNull String streamName, @NonNull Connection conn, @Nullable JetStreamOptions jso, @Nullable JetStreamManagement jsm) throws IOException, JetStreamApiException { - this.streamName = streamName; + private CounterContext(@NonNull String streamName, + @NonNull Connection conn, + @Nullable JetStreamOptions jso, + @Nullable JetStreamManagement jsm, + @Nullable StreamInfo si + ) throws IOException, JetStreamApiException + { this.jsm = jsm == null ? conn.jetStreamManagement(jso) : jsm; js = this.jsm.jetStream(); - dbCtx = new DirectBatchContext(conn, jso, streamName); + + if (si == null) { + this.streamName = required(streamName, "Stream name required,"); + si = this.jsm.getStreamInfo(streamName); + } + else { + this.streamName = si.getConfiguration().getName(); + } + + if (!si.getConfiguration().getAllowDirect()) { + throw new IllegalArgumentException("Stream must have allow direct set."); + } + + if (!si.getConfiguration().getAllowMessageCounter()) { + throw new IllegalArgumentException("Stream must have allow message counter set."); + } + + dbCtx = new DirectBatchContext(conn, jso, streamName, si); } private BigInteger _add(String subject, String sv) throws IOException, JetStreamApiException { @@ -114,10 +137,21 @@ public BigInteger zero(String subject) throws JetStreamApiException, IOException } public BigInteger get(String subject) throws JetStreamApiException, IOException { - MessageInfo mi = jsm.getLastMessage(streamName, subject); + MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); return new BigInteger(extractVal(mi.getData())); } + public LinkedBlockingQueue getValues(String... subjects) { + return getValues(Arrays.asList(subjects)); + } + + public LinkedBlockingQueue getValues(List subjects) { + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); + dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterValue(mi))); + return queue; + } + public CounterEntry getEntry(String subject) throws JetStreamApiException, IOException { MessageInfo mi = jsm.getLastMessage(streamName, subject); return new CounterEntry(mi); diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counter/src/main/java/io/synadia/counter/CounterEntry.java index a547885..351d4bd 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntry.java +++ b/counter/src/main/java/io/synadia/counter/CounterEntry.java @@ -38,7 +38,7 @@ public class CounterEntry { } /** - * Whether this CounterEntry is a regular entry + * Whether this CounterEntry is a regular entry as opposed to an error/status * @return true if the CounterEntry is a regular entry */ public boolean isEntry() { diff --git a/counter/src/main/java/io/synadia/counter/CounterValue.java b/counter/src/main/java/io/synadia/counter/CounterValue.java new file mode 100644 index 0000000..474aefa --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterValue.java @@ -0,0 +1,92 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import io.nats.client.support.Status; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.math.BigInteger; + +import static io.synadia.counter.CounterUtils.extractVal; + +public class CounterValue { + public final String subject; + public final BigInteger value; + public final Status status; + + CounterValue(MessageInfo mi) { + this.status = mi.getStatus(); + if (mi.isMessage()) { + this.subject = mi.getSubject(); + this.value = new BigInteger(extractVal(mi.getData())); + } + else { + this.subject = ""; + this.value = BigInteger.ZERO; + } + } + + /** + * Whether this CounterValue is a regular value as opposed to an error/status + * @return true if the CounterEntry is a regular value + */ + public boolean isValue() { + return status == null; + } + + /** + * Whether this CounterEntry is a status message + * @return true if this CounterEntry is a status message + */ + public boolean isStatus() { + return status != null; + } + + /** + * Whether this CounterEntry is a status message and is a direct EOB status + * @return true if this CounterEntry is a status message and is a direct EOB status + */ + public boolean isEobStatus() { + return status != null && status.isEob(); + } + + /** + * Whether this CounterEntry is a status message and is an error status + * @return true if this CounterEntry is a status message and is an error status + */ + public boolean isErrorStatus() { + return status != null && !status.isEob(); + } + + @NonNull + public String getSubject() { + return subject; + } + + @NonNull + public BigInteger getValue() { + return value; + } + + @Nullable + public Status getStatus() { + return status; + } + + @Override + public String toString() { + if (isValue()) { + return "CounterValue{" + + "subject='" + subject + '\'' + + ", value=" + value + + '}'; + } + + return "CounterValue{" + + "status=" + status + + '}'; + } +} From 63b68a7d6b3a18b140b463955847dff241ca0e26 Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 24 Aug 2025 08:08:14 -0400 Subject: [PATCH 020/135] update all jnats to same --- chaos-runner/build.gradle | 3 ++- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index a36b7a1..1eb686a 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -40,7 +40,8 @@ repositories { dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' - implementation 'io.nats:jnats:2.21.5' // this is only for the example and the uber jar won't include it + // this is only for the example and the uber jar won't include it + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index 92b15ef..cb5376a 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index 10ce07a..033c011 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.5' + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index f862338..ba1fc02 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.5' + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index 7049817..e1be3ce 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.5' + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' From 1d90adf1b7af377936d88768bf0bf32d47b3351d Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 25 Aug 2025 08:07:16 -0400 Subject: [PATCH 021/135] update counter api --- .../examples/CounterContextExample.java | 71 +++++++--------- .../synadia/examples/CounterMoreExample.java | 74 +++++++++++++++++ .../synadia/counter/CounterBaseResponse.java | 45 ++++++++++ .../io/synadia/counter/CounterContext.java | 41 +++++++--- .../java/io/synadia/counter/CounterEntry.java | 82 +++---------------- .../synadia/counter/CounterEntryResponse.java | 32 ++++++++ .../synadia/counter/CounterValueResponse.java | 36 ++++++++ 7 files changed, 257 insertions(+), 124 deletions(-) create mode 100644 counter/src/examples/java/io/synadia/examples/CounterMoreExample.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterBaseResponse.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterEntryResponse.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterValueResponse.java diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index 72c8d2e..34a7990 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -9,8 +9,8 @@ import io.nats.client.Nats; import io.nats.client.api.StreamConfiguration; import io.synadia.counter.CounterContext; -import io.synadia.counter.CounterEntry; -import io.synadia.counter.CounterValue; +import io.synadia.counter.CounterEntryResponse; +import io.synadia.counter.CounterValueResponse; import java.math.BigInteger; import java.util.concurrent.LinkedBlockingQueue; @@ -65,31 +65,34 @@ public static void main(String[] args) throws Exception { System.out.println(" get(\"not-found\") -> " + e); } + System.out.println("\n2.3: get() with a default when the subject is not found"); + System.out.println(" get(\"\"not-found\", BigInteger.ZERO\") -> " + counter.getOrElse("not-found", BigInteger.ZERO)); + System.out.println("\n3: getEntry() - The full CounterEntry for a subject, notice the last increment..."); System.out.println(" getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); - System.out.println("\n4.1: getValues() - Get the CounterValue object for multiple subjects. Maybe to total them up?\""); - LinkedBlockingQueue qv = counter.getValues(SUBJECT_A, SUBJECT_B, SUBJECT_C); + System.out.println("\n4.1: getMany() - Get the CounterValue/Response object for multiple subjects. Maybe to total them up?\""); + LinkedBlockingQueue vResponses = counter.getMany(SUBJECT_A, SUBJECT_B, SUBJECT_C); BigInteger total = BigInteger.ZERO; - CounterValue value = qv.poll(1, TimeUnit.SECONDS); - while (value != null && value.isValue()) { - System.out.println(" " + value); - total = total.add(value.value); - value = qv.poll(10, TimeUnit.MILLISECONDS); + CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); + while (vr != null && vr.isValue()) { + System.out.println(" " + vr); + total = total.add(vr.getValue()); + vr = vResponses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" The iteration is signaled done when the CounterValue is a status: " + value); + System.out.println(" The iteration is signaled done when the CounterValueResponse is a status: " + vr); System.out.println(" Values totaled: " + total); - System.out.println("\n4.2: getEntries() - Get CounterEntry for multiple subjects."); - LinkedBlockingQueue qe = counter.getEntries(SUBJECT_A, SUBJECT_B, SUBJECT_C); - CounterEntry entry = qe.poll(1, TimeUnit.SECONDS); + System.out.println("\n4.2: getEntries() - Get CounterEntry/Response for multiple subjects."); + LinkedBlockingQueue responses = counter.getEntries(SUBJECT_A, SUBJECT_B, SUBJECT_C); + CounterEntryResponse entry = responses.poll(1, TimeUnit.SECONDS); while (entry != null && entry.isEntry()) { System.out.println(" " + entry); - entry = qe.poll(10, TimeUnit.MILLISECONDS); + entry = responses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" CounterEntry status 204 signals no more entries: " + entry); + System.out.println(" CounterEntryResponse status EOB signals no more entries: " + entry); System.out.println("\n5.1: set() - Set the value for a subject"); System.out.println(" set(\"" + SUBJECT_A + "\", 9) -> " + counter.set(SUBJECT_A, 9)); @@ -113,41 +116,23 @@ public static void main(String[] args) throws Exception { System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); - System.out.println("\n7.1: getValues() - Get multiple CounterValue - but no subjects have counters"); - qv = counter.getValues("no-counters", "also-counters"); - value = qv.poll(1, TimeUnit.SECONDS); - while (value != null && value.isValue()) { - System.out.println(" " + value); - value = qv.poll(10, TimeUnit.MILLISECONDS); - } - System.out.println(" CounterValue status 204 signals no more entries: " + value); - - System.out.println("\n7.2: getEntries() - Get multiple CounterEntry - but no subjects have counters"); - qe = counter.getEntries("no-counters", "also-counters"); - entry = qe.poll(1, TimeUnit.SECONDS); + System.out.println("\n7: getEntries() - Get multiple CounterEntry/Response - but no subjects have counters"); + responses = counter.getEntries("no-counters", "also-counters"); + entry = responses.poll(1, TimeUnit.SECONDS); while (entry != null && entry.isEntry()) { System.out.println(" " + entry); - entry = qe.poll(10, TimeUnit.MILLISECONDS); - } - System.out.println(" The only CounterEntry received was a 404: " + entry); - - System.out.println("\n8.1: getValues() - Get multiple CounterValue - some subjects have any counters"); - qv = counter.getValues("no-counters", SUBJECT_A, SUBJECT_B, SUBJECT_C); - value = qv.poll(1, TimeUnit.SECONDS); - while (value != null && value.isValue()) { - System.out.println(" " + value); - value = qv.poll(10, TimeUnit.MILLISECONDS); + entry = responses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" CounterValue status 204 signals no more entries: " + value); + System.out.println(" The only CounterEntryResponse received was a 404: " + entry); - System.out.println("\n8.2: getEntries() - Get multiple CounterEntry - some subjects have any counters"); - qe = counter.getEntries("no-counters", SUBJECT_A, SUBJECT_B, SUBJECT_C); - entry = qe.poll(1, TimeUnit.SECONDS); + System.out.println("\n8: getEntries() - Get multiple CounterEntry/Response - some subjects have any counters"); + responses = counter.getEntries("no-counters", SUBJECT_A, SUBJECT_B, SUBJECT_C); + entry = responses.poll(1, TimeUnit.SECONDS); while (entry != null && entry.isEntry()) { System.out.println(" " + entry); - entry = qe.poll(10, TimeUnit.MILLISECONDS); + entry = responses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" CounterEntry status 204 signals no more entries: " + entry); + System.out.println(" CounterEntryResponse status EOB signals no more entries: " + entry); } } } diff --git a/counter/src/examples/java/io/synadia/examples/CounterMoreExample.java b/counter/src/examples/java/io/synadia/examples/CounterMoreExample.java new file mode 100644 index 0000000..89b65bb --- /dev/null +++ b/counter/src/examples/java/io/synadia/examples/CounterMoreExample.java @@ -0,0 +1,74 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.Nats; +import io.nats.client.api.StreamConfiguration; +import io.synadia.counter.CounterContext; +import io.synadia.counter.CounterValueResponse; + +import java.math.BigInteger; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +public class CounterMoreExample { + + public static void main(String[] args) throws Exception { + try (Connection nc = Nats.connect()) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Set up a fresh counter stream + try { jsm.deleteStream("more-stream"); } catch (JetStreamApiException ignore) {} + CounterContext counter = CounterContext.createCounterStream(nc, + StreamConfiguration.builder() + .name("more-stream") + .subjects("more.*") + .build()); + + System.out.println("1: Add some values..."); + System.out.println(" add(\"more.A\", 5) -> " + counter.add("more.A", 5)); + System.out.println(" add(\"more.B\", 50) -> " + counter.add("more.B", 50)); + System.out.println(" add(\"more.C\", 500) -> " + counter.add("more.C", 500)); + System.out.println(" add(\"more.D\", 5000) -> " + counter.add("more.D", 5000)); + System.out.println(" add(\"more.E\", 50000) -> " + counter.add("more.E", 50000)); + + System.out.println("\n2: getMany() - Get the CounterValue/Response object for multiple subjects.\""); + LinkedBlockingQueue vResponses = counter.getMany("more.*"); + BigInteger addTotal = BigInteger.ZERO; + BigInteger multiplyTotal = BigInteger.ONE; + CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); + while (vr != null && vr.isValue()) { + System.out.println(" " + vr); + addTotal = addTotal.add(vr.getValue()); + multiplyTotal = multiplyTotal.multiply(vr.getValue()); + vr = vResponses.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" The iteration is signaled done when the CounterValueResponse is a status: " + vr); + System.out.println(" Values added totaled: " + addTotal); + System.out.println(" Values multiplied totaled: " + multiplyTotal); + + System.out.println("\n3: getOrElse() returns the 'else' value if the subject is not found."); + try { + counter.get("more.or-else"); + } + catch (JetStreamApiException e) { + System.out.println(" get(\"more.or-else\") -> " + e); + } + System.out.println(" getOrElse(\"more.or-else\", 77777) -> " + counter.getOrElse("more.or-else", 77777)); + + System.out.println("\n4: It's safe to call set() even if the subject did not exist."); + try { + counter.get("more.not-found"); + } + catch (JetStreamApiException e) { + System.out.println(" get(\"more.not-found\") -> " + e); + } + System.out.println(" set(\"more.not-found\", 99999) -> " + counter.add("more.not-found", 99999)); + System.out.println(" get(\"more.not-found\") -> " + counter.get("more.not-found")); + } + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterBaseResponse.java b/counter/src/main/java/io/synadia/counter/CounterBaseResponse.java new file mode 100644 index 0000000..3abf61e --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterBaseResponse.java @@ -0,0 +1,45 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import io.nats.client.support.Status; +import org.jspecify.annotations.Nullable; + +abstract class CounterBaseResponse { + protected final MessageInfo mi; + + CounterBaseResponse(MessageInfo mi) { + this.mi = mi; + } + + @Nullable + public Status getStatus() { + return mi.getStatus(); + } + + /** + * Whether this CounterEntry is a status message + * @return true if this CounterEntry is a status message + */ + public boolean isStatus() { + return mi.isStatus(); + } + + /** + * Whether this CounterEntry is a status message and is a direct EOB status + * @return true if this CounterEntry is a status message and is a direct EOB status + */ + public boolean isEobStatus() { + return mi.isEobStatus(); + } + + /** + * Whether this CounterEntry is a status message and is an error status + * @return true if this CounterEntry is a status message and is an error status + */ + public boolean isErrorStatus() { + return mi.isErrorStatus(); + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterContext.java b/counter/src/main/java/io/synadia/counter/CounterContext.java index d0ae876..9347876 100644 --- a/counter/src/main/java/io/synadia/counter/CounterContext.java +++ b/counter/src/main/java/io/synadia/counter/CounterContext.java @@ -127,8 +127,7 @@ public BigInteger set(String subject, long value) throws JetStreamApiException, } public BigInteger set(String subject, BigInteger value) throws JetStreamApiException, IOException { - // 100 -> 200 = add(200 - 100) | 100 -> 0 = add(0 - 100) | -100 -> 0 = add(0 - -100) | -100 -> 200 = add(200 - -100) - BigInteger bi = get(subject); + BigInteger bi = getOrElse(subject, BigInteger.ZERO); return _add(subject, value.subtract(bi).toString()); } @@ -141,14 +140,32 @@ public BigInteger get(String subject) throws JetStreamApiException, IOException return new BigInteger(extractVal(mi.getData())); } - public LinkedBlockingQueue getValues(String... subjects) { - return getValues(Arrays.asList(subjects)); + public BigInteger getOrElse(String subject, int dflt) { + return getOrElse(subject, BigInteger.valueOf(dflt)); } - public LinkedBlockingQueue getValues(List subjects) { - LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); - MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); - dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterValue(mi))); + public BigInteger getOrElse(String subject, long dflt) { + return getOrElse(subject, BigInteger.valueOf(dflt)); + } + + public BigInteger getOrElse(String subject, BigInteger dflt) { + try { + MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); + return new BigInteger(extractVal(mi.getData())); + } + catch (IOException | JetStreamApiException e) { + return dflt; + } + } + + public LinkedBlockingQueue getMany(String... subjects) { + return getMany(Arrays.asList(subjects)); + } + + public LinkedBlockingQueue getMany(List subjects) { + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects).noHeaders(); + dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterValueResponse(mi))); return queue; } @@ -157,14 +174,14 @@ public CounterEntry getEntry(String subject) throws JetStreamApiException, IOExc return new CounterEntry(mi); } - public LinkedBlockingQueue getEntries(String... subjects) { + public LinkedBlockingQueue getEntries(String... subjects) { return getEntries(Arrays.asList(subjects)); } - public LinkedBlockingQueue getEntries(List subjects) { - LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + public LinkedBlockingQueue getEntries(List subjects) { + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); - dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterEntry(mi))); + dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterEntryResponse(mi))); return queue; } } diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counter/src/main/java/io/synadia/counter/CounterEntry.java index 351d4bd..f913859 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntry.java +++ b/counter/src/main/java/io/synadia/counter/CounterEntry.java @@ -4,9 +4,7 @@ package io.synadia.counter; import io.nats.client.api.MessageInfo; -import io.nats.client.support.Status; import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; import java.math.BigInteger; import java.util.HashMap; @@ -16,97 +14,43 @@ import static io.synadia.counter.CounterUtils.extractVal; public class CounterEntry { - public final String subject; - public final BigInteger value; - public final BigInteger lastIncrement; - public final Map sources; - public final Status status; + public final MessageInfo mi; CounterEntry(MessageInfo mi) { - this.status = mi.getStatus(); - this.sources = new HashMap<>(); - if (mi.isMessage()) { - this.subject = mi.getSubject(); - this.value = new BigInteger(extractVal(mi.getData())); - this.lastIncrement = new BigInteger(extractIncrement(mi.getHeaders())); + if (!mi.isMessage()) { + throw new IllegalArgumentException("Message is not a counter message"); } - else { - this.subject = ""; - this.value = BigInteger.ZERO; - this.lastIncrement = BigInteger.ZERO; - } - } - - /** - * Whether this CounterEntry is a regular entry as opposed to an error/status - * @return true if the CounterEntry is a regular entry - */ - public boolean isEntry() { - return status == null; - } - - /** - * Whether this CounterEntry is a status message - * @return true if this CounterEntry is a status message - */ - public boolean isStatus() { - return status != null; - } - - /** - * Whether this CounterEntry is a status message and is a direct EOB status - * @return true if this CounterEntry is a status message and is a direct EOB status - */ - public boolean isEobStatus() { - return status != null && status.isEob(); - } - - /** - * Whether this CounterEntry is a status message and is an error status - * @return true if this CounterEntry is a status message and is an error status - */ - public boolean isErrorStatus() { - return status != null && !status.isEob(); + this.mi = mi; } @NonNull public String getSubject() { - return subject; + //noinspection DataFlowIssue we know it's not null + return mi.getSubject(); } @NonNull public BigInteger getValue() { - return value; + return new BigInteger(extractVal(mi.getData())); } @NonNull public BigInteger getLastIncrement() { - return lastIncrement; + return new BigInteger(extractIncrement(mi.getHeaders())); } @NonNull public Map getSources() { - return sources; - } - - @Nullable - public Status getStatus() { - return status; + return new HashMap<>(); // TODO } @Override public String toString() { - if (isEntry()) { - return "CounterEntry{" + - "subject='" + subject + '\'' + - ", value=" + value + - ", lastIncrement=" + lastIncrement + - ", sources=" + sources + - '}'; - } - return "CounterEntry{" + - "status=" + status + + "subject='" + getSubject() + '\'' + + ", value=" + getValue() + + ", lastIncrement=" + getLastIncrement() + + ", sources=" + getSources() + '}'; } } diff --git a/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java b/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java new file mode 100644 index 0000000..6f6fc23 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java @@ -0,0 +1,32 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import org.jspecify.annotations.Nullable; + +public class CounterEntryResponse extends CounterBaseResponse { + + CounterEntryResponse(MessageInfo mi) { + super(mi); + } + + /** + * Whether this CounterEntry is a regular entry as opposed to an error/status + * @return true if the CounterEntry is a regular entry + */ + public boolean isEntry() { + return mi.isMessage(); + } + + @Nullable + public CounterEntry getEntry() { + return mi.isMessage() ? new CounterEntry(mi) : null; + } + + @Override + public String toString() { + return "CounterEntryResponse{ " + (isEntry() ? getEntry() : getStatus() ) + " }"; + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java new file mode 100644 index 0000000..0ea5633 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java @@ -0,0 +1,36 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import org.jspecify.annotations.Nullable; + +import java.math.BigInteger; + +import static io.synadia.counter.CounterUtils.extractVal; + +public class CounterValueResponse extends CounterBaseResponse { + + CounterValueResponse(MessageInfo mi) { + super(mi); + } + + /** + * Whether this CounterValue is a regular value as opposed to an error/status + * @return true if the CounterValue is a regular value + */ + public boolean isValue() { + return mi.isMessage(); + } + + @Nullable + public BigInteger getValue() { + return mi.isMessage() ? new BigInteger(extractVal(mi.getData())) : null; + } + + @Override + public String toString() { + return "CounterValueResponse{ " + (isValue() ? getValue() : getStatus() ) + " }"; + } +} From 92fceba22418c98645d32d3827fcf125dde20683 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 25 Aug 2025 08:08:01 -0400 Subject: [PATCH 022/135] update counter api --- .../java/io/synadia/counter/CounterEntryResponse.java | 2 +- .../{CounterBaseResponse.java => CounterResponse.java} | 10 +++++----- .../java/io/synadia/counter/CounterValueResponse.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename counter/src/main/java/io/synadia/counter/{CounterBaseResponse.java => CounterResponse.java} (76%) diff --git a/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java b/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java index 6f6fc23..b76e351 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java +++ b/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java @@ -6,7 +6,7 @@ import io.nats.client.api.MessageInfo; import org.jspecify.annotations.Nullable; -public class CounterEntryResponse extends CounterBaseResponse { +public class CounterEntryResponse extends CounterResponse { CounterEntryResponse(MessageInfo mi) { super(mi); diff --git a/counter/src/main/java/io/synadia/counter/CounterBaseResponse.java b/counter/src/main/java/io/synadia/counter/CounterResponse.java similarity index 76% rename from counter/src/main/java/io/synadia/counter/CounterBaseResponse.java rename to counter/src/main/java/io/synadia/counter/CounterResponse.java index 3abf61e..8310153 100644 --- a/counter/src/main/java/io/synadia/counter/CounterBaseResponse.java +++ b/counter/src/main/java/io/synadia/counter/CounterResponse.java @@ -7,10 +7,10 @@ import io.nats.client.support.Status; import org.jspecify.annotations.Nullable; -abstract class CounterBaseResponse { +abstract class CounterResponse { protected final MessageInfo mi; - CounterBaseResponse(MessageInfo mi) { + CounterResponse(MessageInfo mi) { this.mi = mi; } @@ -20,7 +20,7 @@ public Status getStatus() { } /** - * Whether this CounterEntry is a status message + * Whether this response is a status message * @return true if this CounterEntry is a status message */ public boolean isStatus() { @@ -28,7 +28,7 @@ public boolean isStatus() { } /** - * Whether this CounterEntry is a status message and is a direct EOB status + * Whether this response is a status message and is a direct EOB status * @return true if this CounterEntry is a status message and is a direct EOB status */ public boolean isEobStatus() { @@ -36,7 +36,7 @@ public boolean isEobStatus() { } /** - * Whether this CounterEntry is a status message and is an error status + * Whether this response is a status message and is an error status * @return true if this CounterEntry is a status message and is an error status */ public boolean isErrorStatus() { diff --git a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java index 0ea5633..ab9e323 100644 --- a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java +++ b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java @@ -10,7 +10,7 @@ import static io.synadia.counter.CounterUtils.extractVal; -public class CounterValueResponse extends CounterBaseResponse { +public class CounterValueResponse extends CounterResponse { CounterValueResponse(MessageInfo mi) { super(mi); From a7da2b94384cc61828162824bae1746f7825dc4b Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 25 Aug 2025 08:48:36 -0400 Subject: [PATCH 023/135] better examples --- .../examples/CounterContextExample.java | 204 +++++++++++------- .../synadia/examples/CounterMoreExample.java | 74 ------- .../io/synadia/counter/CounterContext.java | 6 +- .../java/io/synadia/counter/CounterEntry.java | 2 +- 4 files changed, 133 insertions(+), 153 deletions(-) delete mode 100644 counter/src/examples/java/io/synadia/examples/CounterMoreExample.java diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index 34a7990..de7855e 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -18,121 +18,173 @@ public class CounterContextExample { - private static final String STREAM_NAME = "counter-stream"; - private static final String STREAM_SUBJECT = "cs.*"; - private static final String SUBJECT_A = "cs.A"; - private static final String SUBJECT_B = "cs.B"; - private static final String SUBJECT_C = "cs.C"; - public static void main(String[] args) throws Exception { try (Connection nc = Nats.connect()) { JetStreamManagement jsm = nc.jetStreamManagement(); // Set up a fresh counter stream - try { jsm.deleteStream(STREAM_NAME); } catch (JetStreamApiException ignore) {} + try { jsm.deleteStream("counter-stream"); } catch (JetStreamApiException ignore) {} CounterContext counter = CounterContext.createCounterStream(nc, StreamConfiguration.builder() - .name(STREAM_NAME) - .subjects(STREAM_SUBJECT) + .name("counter-stream") + .subjects("cs.*") .build()); + // ---------------------------------------------------------------------------------------------------- System.out.println("1: Add to a subject..."); - System.out.println(" add(\"" + SUBJECT_A + "\", 1) -> " + counter.add(SUBJECT_A, 1)); - System.out.println(" add(\"" + SUBJECT_A + "\", 2) -> " + counter.add(SUBJECT_A, 2)); - System.out.println(" add(\"" + SUBJECT_A + "\", 3) -> " + counter.add(SUBJECT_A, 3)); - System.out.println(" add(\"" + SUBJECT_A + "\", -1) -> " + counter.add(SUBJECT_A, -1)); - - System.out.println(" add(\"" + SUBJECT_B + "\", 10) -> " + counter.add(SUBJECT_B, 10)); - System.out.println(" add(\"" + SUBJECT_B + "\", 20) -> " + counter.add(SUBJECT_B, 20)); - System.out.println(" add(\"" + SUBJECT_B + "\", 30) -> " + counter.add(SUBJECT_B, 30)); - System.out.println(" add(\"" + SUBJECT_B + "\", -10) -> " + counter.add(SUBJECT_B, -10)); - - System.out.println(" add(\"" + SUBJECT_C + "\", 100) -> " + counter.add(SUBJECT_C, 100)); - System.out.println(" add(\"" + SUBJECT_C + "\", 200) -> " + counter.add(SUBJECT_C, 200)); - System.out.println(" add(\"" + SUBJECT_C + "\", 300) -> " + counter.add(SUBJECT_C, 300)); - System.out.println(" add(\"" + SUBJECT_C + "\", -100) -> " + counter.add(SUBJECT_C, -100)); - + System.out.println(" add(\"cs.A\", 1) -> " + counter.add("cs.A", 1)); + System.out.println(" add(\"cs.A\", 2) -> " + counter.add("cs.A", 2)); + System.out.println(" add(\"cs.A\", 3) -> " + counter.add("cs.A", 3)); + System.out.println(" add(\"cs.A\", -1) -> " + counter.add("cs.A", -1)); + + System.out.println(" add(\"cs.B\", 10) -> " + counter.add("cs.B", 10)); + System.out.println(" add(\"cs.B\", 20) -> " + counter.add("cs.B", 20)); + System.out.println(" add(\"cs.B\", 30) -> " + counter.add("cs.B", 30)); + System.out.println(" add(\"cs.B\", -10) -> " + counter.add("cs.B", -10)); + + System.out.println(" add(\"cs.C\", 100) -> " + counter.add("cs.C", 100)); + System.out.println(" add(\"cs.C\", 200) -> " + counter.add("cs.C", 200)); + System.out.println(" add(\"cs.C\", 300) -> " + counter.add("cs.C", 300)); + System.out.println(" add(\"cs.C\", -100) -> " + counter.add("cs.C", -100)); + + // ---------------------------------------------------------------------------------------------------- System.out.println("\n2.1: get() for existing subjects"); - System.out.println(" get(\"" + SUBJECT_A + "\") -> " + counter.get(SUBJECT_A)); - System.out.println(" get(\"" + SUBJECT_B + "\") -> " + counter.get(SUBJECT_B)); - System.out.println(" get(\"" + SUBJECT_C + "\") -> " + counter.get(SUBJECT_C)); + System.out.println(" get(\"cs.A\") -> " + counter.get("cs.A")); + System.out.println(" get(\"cs.B\") -> " + counter.get("cs.B")); + System.out.println(" get(\"cs.C\") -> " + counter.get("cs.C")); System.out.println("\n2.2: get() when the subject is not found"); try { counter.get("not-found"); } catch (JetStreamApiException e) { - System.out.println(" get(\"not-found\") -> " + e); + System.out.println(" get(\"not-found\") -> " + e); + } + + System.out.println("\n2.3: get() for a single subject does not allow wildcards"); + try { + counter.get("cs.*"); + } + catch (RuntimeException e) { + System.out.println(" get(\"cs.*\") -> " + e); } System.out.println("\n2.3: get() with a default when the subject is not found"); - System.out.println(" get(\"\"not-found\", BigInteger.ZERO\") -> " + counter.getOrElse("not-found", BigInteger.ZERO)); + System.out.println(" get(\"not-found\", BigInteger.ZERO\") -> " + counter.getOrElse("not-found", BigInteger.ZERO)); + + System.out.println("\n2.4: get() with a default when the subject IS found"); + System.out.println(" get(\"cs.C\", BigInteger.ZERO\") -> " + counter.getOrElse("cs.C", BigInteger.ZERO)); + // ---------------------------------------------------------------------------------------------------- System.out.println("\n3: getEntry() - The full CounterEntry for a subject, notice the last increment..."); - System.out.println(" getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); - System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); - System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); + System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); + System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); - System.out.println("\n4.1: getMany() - Get the CounterValue/Response object for multiple subjects. Maybe to total them up?\""); - LinkedBlockingQueue vResponses = counter.getMany(SUBJECT_A, SUBJECT_B, SUBJECT_C); + // ---------------------------------------------------------------------------------------------------- + System.out.println("\n4.1: getMany(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterValue/Response object for multiple subjects. Maybe to total them up?\""); + LinkedBlockingQueue vResponses = counter.getMany("cs.A", "cs.B", "cs.C"); BigInteger total = BigInteger.ZERO; CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); while (vr != null && vr.isValue()) { - System.out.println(" " + vr); + System.out.println(" " + vr); total = total.add(vr.getValue()); vr = vResponses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" The iteration is signaled done when the CounterValueResponse is a status: " + vr); - System.out.println(" Values totaled: " + total); - - System.out.println("\n4.2: getEntries() - Get CounterEntry/Response for multiple subjects."); - LinkedBlockingQueue responses = counter.getEntries(SUBJECT_A, SUBJECT_B, SUBJECT_C); - CounterEntryResponse entry = responses.poll(1, TimeUnit.SECONDS); - while (entry != null && entry.isEntry()) { - System.out.println(" " + entry); - entry = responses.poll(10, TimeUnit.MILLISECONDS); + System.out.println(" " + vr + " -> No more entries."); + System.out.println(" Values totaled: " + total); + + System.out.println("\n4.2: getEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get CounterEntry/Response for multiple subjects."); + LinkedBlockingQueue eResponses = counter.getEntries("cs.A", "cs.B", "cs.C"); + CounterEntryResponse er = eResponses.poll(1, TimeUnit.SECONDS); + while (er != null && er.isEntry()) { + System.out.println(" " + er); + er = eResponses.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" " + er + " -> No more entries."); + + System.out.println("\n4.3: getMany(\"cs.*\") - Get the CounterValue/Response for wildcard subject(s)."); + vResponses = counter.getMany("cs.*"); + vr = vResponses.poll(1, TimeUnit.SECONDS); + while (vr != null && vr.isValue()) { + System.out.println(" " + vr); + vr = vResponses.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" " + vr + " -> No more entries."); + + System.out.println("\n4.4: getEntries(\"cs.*\") - Get CounterEntry/Response for wildcard subject(s)."); + eResponses = counter.getEntries("cs.*"); + er = eResponses.poll(1, TimeUnit.SECONDS); + while (er != null && er.isEntry()) { + System.out.println(" " + er); + er = eResponses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" CounterEntryResponse status EOB signals no more entries: " + entry); + System.out.println(" " + er + " -> No more entries."); + // ---------------------------------------------------------------------------------------------------- System.out.println("\n5.1: set() - Set the value for a subject"); - System.out.println(" set(\"" + SUBJECT_A + "\", 9) -> " + counter.set(SUBJECT_A, 9)); - System.out.println(" set(\"" + SUBJECT_B + "\", 99) -> " + counter.set(SUBJECT_B, 99)); - System.out.println(" set(\"" + SUBJECT_C + "\", 999) -> " + counter.set(SUBJECT_C, 999)); + System.out.println(" set(\"cs.A\", 9) -> " + counter.set("cs.A", 9)); + System.out.println(" set(\"cs.B\", 99) -> " + counter.set("cs.B", 99)); + System.out.println(" set(\"cs.C\", 999) -> " + counter.set("cs.C", 999)); System.out.println("\n5.2: getEntry() - Get the full CounterEntry, notice the last increment after a set represents" + - "\n the difference between the entry before the set and the set value."); - System.out.println(" getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); - System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); - System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + "\n the difference between the entry before the set and the set value."); + System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); + System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); + System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); - System.out.println("\n6.1: zero() is a shortcut to set the value of a subject to 0"); - System.out.println(" zero(\"" + SUBJECT_A + "\") -> " + counter.zero(SUBJECT_A)); - System.out.println(" zero(\"" + SUBJECT_B + "\") -> " + counter.zero(SUBJECT_B)); - System.out.println(" zero(\"" + SUBJECT_C + "\") -> " + counter.zero(SUBJECT_C)); + // ---------------------------------------------------------------------------------------------------- + System.out.println("\n6.1: zero() is a shortcut to set the value of a subject to 0."); + System.out.println(" zero(\"cs.A\") -> " + counter.zero("cs.A")); + System.out.println(" zero(\"cs.B\") -> " + counter.zero("cs.B")); + System.out.println(" zero(\"cs.C\") -> " + counter.zero("cs.C")); System.out.println("\n6.2: getEntry() - Get the full CounterEntry, notice the last increment after a zero represents" + - "\n the difference between the entry before the zero and zero."); - System.out.println(" getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); - System.out.println(" getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); - System.out.println(" getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); - - System.out.println("\n7: getEntries() - Get multiple CounterEntry/Response - but no subjects have counters"); - responses = counter.getEntries("no-counters", "also-counters"); - entry = responses.poll(1, TimeUnit.SECONDS); - while (entry != null && entry.isEntry()) { - System.out.println(" " + entry); - entry = responses.poll(10, TimeUnit.MILLISECONDS); + "\n the difference between the entry before the zero() and 0."); + System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); + System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); + System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); + + // ---------------------------------------------------------------------------------------------------- + System.out.println("\n7: getEntries(\"no-counters\", \"also-counters\") - Get multiple but no subjects have counters."); + eResponses = counter.getEntries("no-counters", "also-counters"); + er = eResponses.poll(1, TimeUnit.SECONDS); + while (er != null && er.isEntry()) { + System.out.println(" " + er); + er = eResponses.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" " + er); + + // ---------------------------------------------------------------------------------------------------- + System.out.println("\n8: getEntries(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - Get multiple when some subjects have counters."); + eResponses = counter.getEntries("no-counters", "cs.A", "cs.B", "cs.C"); + er = eResponses.poll(1, TimeUnit.SECONDS); + while (er != null && er.isEntry()) { + System.out.println(" " + er); + er = eResponses.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" " + er + " -> No more entries."); + + // ---------------------------------------------------------------------------------------------------- + System.out.println("\n9: getOrElse() returns the 'else' value if the subject is not found."); + try { + counter.get("or-else"); } - System.out.println(" The only CounterEntryResponse received was a 404: " + entry); - - System.out.println("\n8: getEntries() - Get multiple CounterEntry/Response - some subjects have any counters"); - responses = counter.getEntries("no-counters", SUBJECT_A, SUBJECT_B, SUBJECT_C); - entry = responses.poll(1, TimeUnit.SECONDS); - while (entry != null && entry.isEntry()) { - System.out.println(" " + entry); - entry = responses.poll(10, TimeUnit.MILLISECONDS); + catch (JetStreamApiException e) { + System.out.println(" get(\"or-else\") -> " + e); + } + System.out.println(" getOrElse(\"or-else\", 77777) -> " + counter.getOrElse("or-else", 77777)); + + // ---------------------------------------------------------------------------------------------------- + System.out.println("\n10: It's safe to call set() even if the subject did not exist."); + try { + counter.get("cs.X"); + } + catch (JetStreamApiException e) { + System.out.println(" get(\"cs.X\") -> " + e); } - System.out.println(" CounterEntryResponse status EOB signals no more entries: " + entry); + System.out.println(" set(\"cs.X\", 99999) -> " + counter.add("cs.X", 99999)); + System.out.println(" get(\"cs.X\") -> " + counter.get("cs.X")); } } } diff --git a/counter/src/examples/java/io/synadia/examples/CounterMoreExample.java b/counter/src/examples/java/io/synadia/examples/CounterMoreExample.java deleted file mode 100644 index 89b65bb..0000000 --- a/counter/src/examples/java/io/synadia/examples/CounterMoreExample.java +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. -// See LICENSE and NOTICE file for details. - -package io.synadia.examples; - -import io.nats.client.Connection; -import io.nats.client.JetStreamApiException; -import io.nats.client.JetStreamManagement; -import io.nats.client.Nats; -import io.nats.client.api.StreamConfiguration; -import io.synadia.counter.CounterContext; -import io.synadia.counter.CounterValueResponse; - -import java.math.BigInteger; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -public class CounterMoreExample { - - public static void main(String[] args) throws Exception { - try (Connection nc = Nats.connect()) { - JetStreamManagement jsm = nc.jetStreamManagement(); - - // Set up a fresh counter stream - try { jsm.deleteStream("more-stream"); } catch (JetStreamApiException ignore) {} - CounterContext counter = CounterContext.createCounterStream(nc, - StreamConfiguration.builder() - .name("more-stream") - .subjects("more.*") - .build()); - - System.out.println("1: Add some values..."); - System.out.println(" add(\"more.A\", 5) -> " + counter.add("more.A", 5)); - System.out.println(" add(\"more.B\", 50) -> " + counter.add("more.B", 50)); - System.out.println(" add(\"more.C\", 500) -> " + counter.add("more.C", 500)); - System.out.println(" add(\"more.D\", 5000) -> " + counter.add("more.D", 5000)); - System.out.println(" add(\"more.E\", 50000) -> " + counter.add("more.E", 50000)); - - System.out.println("\n2: getMany() - Get the CounterValue/Response object for multiple subjects.\""); - LinkedBlockingQueue vResponses = counter.getMany("more.*"); - BigInteger addTotal = BigInteger.ZERO; - BigInteger multiplyTotal = BigInteger.ONE; - CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); - while (vr != null && vr.isValue()) { - System.out.println(" " + vr); - addTotal = addTotal.add(vr.getValue()); - multiplyTotal = multiplyTotal.multiply(vr.getValue()); - vr = vResponses.poll(10, TimeUnit.MILLISECONDS); - } - System.out.println(" The iteration is signaled done when the CounterValueResponse is a status: " + vr); - System.out.println(" Values added totaled: " + addTotal); - System.out.println(" Values multiplied totaled: " + multiplyTotal); - - System.out.println("\n3: getOrElse() returns the 'else' value if the subject is not found."); - try { - counter.get("more.or-else"); - } - catch (JetStreamApiException e) { - System.out.println(" get(\"more.or-else\") -> " + e); - } - System.out.println(" getOrElse(\"more.or-else\", 77777) -> " + counter.getOrElse("more.or-else", 77777)); - - System.out.println("\n4: It's safe to call set() even if the subject did not exist."); - try { - counter.get("more.not-found"); - } - catch (JetStreamApiException e) { - System.out.println(" get(\"more.not-found\") -> " + e); - } - System.out.println(" set(\"more.not-found\", 99999) -> " + counter.add("more.not-found", 99999)); - System.out.println(" get(\"more.not-found\") -> " + counter.get("more.not-found")); - } - } -} diff --git a/counter/src/main/java/io/synadia/counter/CounterContext.java b/counter/src/main/java/io/synadia/counter/CounterContext.java index 9347876..9fa926d 100644 --- a/counter/src/main/java/io/synadia/counter/CounterContext.java +++ b/counter/src/main/java/io/synadia/counter/CounterContext.java @@ -136,6 +136,9 @@ public BigInteger zero(String subject) throws JetStreamApiException, IOException } public BigInteger get(String subject) throws JetStreamApiException, IOException { + if (subject.contains("*") || subject.contains(">")) { + throw new IllegalArgumentException("Subject must not contain wildcards '*' or '>'."); + } MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); return new BigInteger(extractVal(mi.getData())); } @@ -150,8 +153,7 @@ public BigInteger getOrElse(String subject, long dflt) { public BigInteger getOrElse(String subject, BigInteger dflt) { try { - MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); - return new BigInteger(extractVal(mi.getData())); + return get(subject); } catch (IOException | JetStreamApiException e) { return dflt; diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counter/src/main/java/io/synadia/counter/CounterEntry.java index f913859..b4180b7 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntry.java +++ b/counter/src/main/java/io/synadia/counter/CounterEntry.java @@ -47,7 +47,7 @@ public Map getSources() { @Override public String toString() { return "CounterEntry{" + - "subject='" + getSubject() + '\'' + + "subject=\"" + getSubject() + '\"' + ", value=" + getValue() + ", lastIncrement=" + getLastIncrement() + ", sources=" + getSources() + From ff1405cf6213440841612edd27bd87161566acdd Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 28 Aug 2025 11:12:51 -0400 Subject: [PATCH 024/135] Counter interface improvements --- .../examples/CounterContextExample.java | 8 ++-- .../{CounterContext.java => Counter.java} | 47 ++++++++++++------- 2 files changed, 34 insertions(+), 21 deletions(-) rename counter/src/main/java/io/synadia/counter/{CounterContext.java => Counter.java} (79%) diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index de7855e..422f4e5 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -8,7 +8,7 @@ import io.nats.client.JetStreamManagement; import io.nats.client.Nats; import io.nats.client.api.StreamConfiguration; -import io.synadia.counter.CounterContext; +import io.synadia.counter.Counter; import io.synadia.counter.CounterEntryResponse; import io.synadia.counter.CounterValueResponse; @@ -24,7 +24,7 @@ public static void main(String[] args) throws Exception { // Set up a fresh counter stream try { jsm.deleteStream("counter-stream"); } catch (JetStreamApiException ignore) {} - CounterContext counter = CounterContext.createCounterStream(nc, + Counter counter = Counter.createCounterStream(nc, StreamConfiguration.builder() .name("counter-stream") .subjects("cs.*") @@ -83,7 +83,7 @@ public static void main(String[] args) throws Exception { // ---------------------------------------------------------------------------------------------------- System.out.println("\n4.1: getMany(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterValue/Response object for multiple subjects. Maybe to total them up?\""); - LinkedBlockingQueue vResponses = counter.getMany("cs.A", "cs.B", "cs.C"); + LinkedBlockingQueue vResponses = counter.getValues("cs.A", "cs.B", "cs.C"); BigInteger total = BigInteger.ZERO; CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); while (vr != null && vr.isValue()) { @@ -104,7 +104,7 @@ public static void main(String[] args) throws Exception { System.out.println(" " + er + " -> No more entries."); System.out.println("\n4.3: getMany(\"cs.*\") - Get the CounterValue/Response for wildcard subject(s)."); - vResponses = counter.getMany("cs.*"); + vResponses = counter.getValues("cs.*"); vr = vResponses.poll(1, TimeUnit.SECONDS); while (vr != null && vr.isValue()) { System.out.println(" " + vr); diff --git a/counter/src/main/java/io/synadia/counter/CounterContext.java b/counter/src/main/java/io/synadia/counter/Counter.java similarity index 79% rename from counter/src/main/java/io/synadia/counter/CounterContext.java rename to counter/src/main/java/io/synadia/counter/Counter.java index 9fa926d..ce1ea8d 100644 --- a/counter/src/main/java/io/synadia/counter/CounterContext.java +++ b/counter/src/main/java/io/synadia/counter/Counter.java @@ -21,13 +21,13 @@ import static io.synadia.counter.CounterUtils.INCREMENT_HEADER; import static io.synadia.counter.CounterUtils.extractVal; -public class CounterContext { +public class Counter { - public static CounterContext createCounterStream(Connection conn, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + public static Counter createCounterStream(Connection conn, StreamConfiguration userConfig) throws JetStreamApiException, IOException { return createCounterStream(conn, null, userConfig); } - public static CounterContext createCounterStream(Connection conn, JetStreamOptions jso, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + public static Counter createCounterStream(Connection conn, JetStreamOptions jso, StreamConfiguration userConfig) throws JetStreamApiException, IOException { if (userConfig.getRetentionPolicy() != RetentionPolicy.Limits) { throw new IllegalArgumentException("Retention Policy - Limits is the only allowed limit for counter streams."); } @@ -42,7 +42,7 @@ public static CounterContext createCounterStream(Connection conn, JetStreamOptio JetStreamManagement jsm = conn.jetStreamManagement(jso); StreamInfo si = jsm.addStream(config); - return new CounterContext(config.getName(), conn, jso, jsm, si); + return new Counter(config.getName(), conn, jso, jsm, si); } private final String streamName; @@ -50,19 +50,19 @@ public static CounterContext createCounterStream(Connection conn, JetStreamOptio private final JetStream js; private final DirectBatchContext dbCtx; - public CounterContext(String streamName, Connection conn) throws IOException, JetStreamApiException { + public Counter(String streamName, Connection conn) throws IOException, JetStreamApiException { this(streamName, conn, null, null, null); } - public CounterContext(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException { + public Counter(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException { this(streamName, conn, jso, null, null); } - private CounterContext(@NonNull String streamName, - @NonNull Connection conn, - @Nullable JetStreamOptions jso, - @Nullable JetStreamManagement jsm, - @Nullable StreamInfo si + private Counter(@NonNull String streamName, + @NonNull Connection conn, + @Nullable JetStreamOptions jso, + @Nullable JetStreamManagement jsm, + @Nullable StreamInfo si ) throws IOException, JetStreamApiException { this.jsm = jsm == null ? conn.jetStreamManagement(jso) : jsm; @@ -136,9 +136,7 @@ public BigInteger zero(String subject) throws JetStreamApiException, IOException } public BigInteger get(String subject) throws JetStreamApiException, IOException { - if (subject.contains("*") || subject.contains(">")) { - throw new IllegalArgumentException("Subject must not contain wildcards '*' or '>'."); - } + validateSingleSubject(subject); MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); return new BigInteger(extractVal(mi.getData())); } @@ -160,11 +158,17 @@ public BigInteger getOrElse(String subject, BigInteger dflt) { } } - public LinkedBlockingQueue getMany(String... subjects) { - return getMany(Arrays.asList(subjects)); + public CounterValue getValue(String subject) throws JetStreamApiException, IOException { + validateSingleSubject(subject); + MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); + return new CounterValue(mi); + } + + public LinkedBlockingQueue getValues(String... subjects) { + return getValues(Arrays.asList(subjects)); } - public LinkedBlockingQueue getMany(List subjects) { + public LinkedBlockingQueue getValues(List subjects) { LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects).noHeaders(); dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterValueResponse(mi))); @@ -186,4 +190,13 @@ public LinkedBlockingQueue getEntries(List subject dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterEntryResponse(mi))); return queue; } + + private static void validateSingleSubject(String subject) { + if (subject == null || subject.isEmpty()) { + throw new IllegalArgumentException("Subject required."); + } + if (subject.contains("*") || subject.contains(">")) { + throw new IllegalArgumentException("Subject must not contain wildcards '*' or '>'."); + } + } } From a70a7f0eefb35075fe490c530f1d6efc01761019 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 28 Aug 2025 16:28:09 -0400 Subject: [PATCH 025/135] adhere to adr --- .../examples/CounterContextExample.java | 131 ++++++++++-------- .../main/java/io/synadia/counter/Counter.java | 45 ++---- .../java/io/synadia/counter/CounterEntry.java | 4 +- .../java/io/synadia/counter/CounterUtils.java | 18 ++- .../java/io/synadia/counter/CounterValue.java | 92 ------------ .../synadia/counter/CounterValueResponse.java | 2 +- 6 files changed, 101 insertions(+), 191 deletions(-) delete mode 100644 counter/src/main/java/io/synadia/counter/CounterValue.java diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index 422f4e5..315c169 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -31,7 +31,7 @@ public static void main(String[] args) throws Exception { .build()); // ---------------------------------------------------------------------------------------------------- - System.out.println("1: Add to a subject..."); + System.out.println("1.1: Add to a subject..."); System.out.println(" add(\"cs.A\", 1) -> " + counter.add("cs.A", 1)); System.out.println(" add(\"cs.A\", 2) -> " + counter.add("cs.A", 2)); System.out.println(" add(\"cs.A\", 3) -> " + counter.add("cs.A", 3)); @@ -55,35 +55,49 @@ public static void main(String[] args) throws Exception { System.out.println("\n2.2: get() when the subject is not found"); try { - counter.get("not-found"); + counter.get("cs.not-found"); } catch (JetStreamApiException e) { - System.out.println(" get(\"not-found\") -> " + e); + System.out.println(" get(\"cs.not-found\") -> " + e); } System.out.println("\n2.3: get() for a single subject does not allow wildcards"); try { counter.get("cs.*"); } - catch (RuntimeException e) { + catch (IllegalArgumentException e) { System.out.println(" get(\"cs.*\") -> " + e); } - System.out.println("\n2.3: get() with a default when the subject is not found"); - System.out.println(" get(\"not-found\", BigInteger.ZERO\") -> " + counter.getOrElse("not-found", BigInteger.ZERO)); + System.out.println("\n2.4: getOrElse() with a default when the subject is found"); + System.out.println(" getOrElse(\"cs.C\", BigInteger.ZERO\") -> " + counter.getOrElse("cs.C", BigInteger.ZERO)); - System.out.println("\n2.4: get() with a default when the subject IS found"); - System.out.println(" get(\"cs.C\", BigInteger.ZERO\") -> " + counter.getOrElse("cs.C", BigInteger.ZERO)); + System.out.println("\n2.5: getOrElse() with a default when the subject is not found"); + try { + counter.get("cs.not-found"); + } + catch (JetStreamApiException e) { + System.out.println(" get(\"cs.not-found\") -> " + e); + } + System.out.println(" getOrElse(\"cs.not-found\", 77777) -> " + counter.getOrElse("cs.not-found", 77777)); // ---------------------------------------------------------------------------------------------------- - System.out.println("\n3: getEntry() - The full CounterEntry for a subject, notice the last increment..."); + System.out.println("\n3.1: getEntry() - The full CounterEntry for a subject, notice the last increment..."); System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); + System.out.println("\n3.2: getEntry() does not allow wildcards"); + try { + counter.getEntry("cs.>"); + } + catch (IllegalArgumentException e) { + System.out.println(" getEntry(\"cs.>\") -> " + e); + } + // ---------------------------------------------------------------------------------------------------- - System.out.println("\n4.1: getMany(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterValue/Response object for multiple subjects. Maybe to total them up?\""); - LinkedBlockingQueue vResponses = counter.getValues("cs.A", "cs.B", "cs.C"); + System.out.println("\n4.1: getMultiple(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterValueResponse objects for multiple subjects. Maybe to total them up?\""); + LinkedBlockingQueue vResponses = counter.getMultiple("cs.A", "cs.B", "cs.C"); BigInteger total = BigInteger.ZERO; CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); while (vr != null && vr.isValue()) { @@ -91,10 +105,10 @@ public static void main(String[] args) throws Exception { total = total.add(vr.getValue()); vr = vResponses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" " + vr + " -> No more entries."); + System.out.println(" " + vr + " -> No more values."); System.out.println(" Values totaled: " + total); - System.out.println("\n4.2: getEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get CounterEntry/Response for multiple subjects."); + System.out.println("\n4.2: getEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterEntryResponse objects for multiple subjects."); LinkedBlockingQueue eResponses = counter.getEntries("cs.A", "cs.B", "cs.C"); CounterEntryResponse er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { @@ -103,16 +117,16 @@ public static void main(String[] args) throws Exception { } System.out.println(" " + er + " -> No more entries."); - System.out.println("\n4.3: getMany(\"cs.*\") - Get the CounterValue/Response for wildcard subject(s)."); - vResponses = counter.getValues("cs.*"); + System.out.println("\n4.3: getMultiple(\"cs.*\") - Get the CounterValueResponse objects for wildcard subject(s)."); + vResponses = counter.getMultiple("cs.*"); vr = vResponses.poll(1, TimeUnit.SECONDS); while (vr != null && vr.isValue()) { System.out.println(" " + vr); vr = vResponses.poll(10, TimeUnit.MILLISECONDS); } - System.out.println(" " + vr + " -> No more entries."); + System.out.println(" " + vr + " -> No more values."); - System.out.println("\n4.4: getEntries(\"cs.*\") - Get CounterEntry/Response for wildcard subject(s)."); + System.out.println("\n4.4: getEntries(\"cs.*\") - Get CounterEntryResponse objects for wildcard subject(s)."); eResponses = counter.getEntries("cs.*"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { @@ -122,32 +136,41 @@ public static void main(String[] args) throws Exception { System.out.println(" " + er + " -> No more entries."); // ---------------------------------------------------------------------------------------------------- - System.out.println("\n5.1: set() - Set the value for a subject"); - System.out.println(" set(\"cs.A\", 9) -> " + counter.set("cs.A", 9)); - System.out.println(" set(\"cs.B\", 99) -> " + counter.set("cs.B", 99)); - System.out.println(" set(\"cs.C\", 999) -> " + counter.set("cs.C", 999)); - - System.out.println("\n5.2: getEntry() - Get the full CounterEntry, notice the last increment after a set represents" + - "\n the difference between the entry before the set and the set value."); + System.out.println("\n5.1: setViaAdd() - Sets the value for a subject by" + + "\n 1) calling getOrElse(subject, BigInteger.ZERO)" + + "\n 2) then calling add with the set value minus the current value."); + System.out.println(" setViaAdd(\"cs.A\", 9) -> " + counter.setViaAdd("cs.A", 9)); + System.out.println(" setViaAdd(\"cs.B\", 99) -> " + counter.setViaAdd("cs.B", 99)); + System.out.println(" setViaAdd(\"cs.C\", 999) -> " + counter.setViaAdd("cs.C", 999)); + + System.out.println("\n5.2: getEntry() - Get the full CounterEntry, notice the last increment after a setViaAdd" + + "\n represents the difference between the entry before the set and the set value."); System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); - // ---------------------------------------------------------------------------------------------------- - System.out.println("\n6.1: zero() is a shortcut to set the value of a subject to 0."); - System.out.println(" zero(\"cs.A\") -> " + counter.zero("cs.A")); - System.out.println(" zero(\"cs.B\") -> " + counter.zero("cs.B")); - System.out.println(" zero(\"cs.C\") -> " + counter.zero("cs.C")); - - System.out.println("\n6.2: getEntry() - Get the full CounterEntry, notice the last increment after a zero represents" + - "\n the difference between the entry before the zero() and 0."); - System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); - System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); - System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); + System.out.println("\n5.3: It's safe to call setViaAdd() even if the subject did not exist because it uses getOrElse;"); + try { + counter.get("cs.did-not-exist"); + } + catch (JetStreamApiException e) { + System.out.println(" get(\"cs.did-not-exist\") -> " + e); + } + System.out.println(" setViaAdd(\"cs.did-not-exist\", 99999) -> " + counter.setViaAdd("cs.did-not-exist", 99999)); + System.out.println(" get(\"cs.did-not-exist\") -> " + counter.get("cs.did-not-exist")); // ---------------------------------------------------------------------------------------------------- - System.out.println("\n7: getEntries(\"no-counters\", \"also-counters\") - Get multiple but no subjects have counters."); - eResponses = counter.getEntries("no-counters", "also-counters"); + System.out.println("\n6.1: getMultiple(\"cs.no-counters\", \"cs.also-counters\") - getMultiple but no subjects have counters."); + vResponses = counter.getMultiple("cs.no-counters", "cs.also-counters"); + vr = vResponses.poll(1, TimeUnit.SECONDS); + while (vr != null && vr.isValue()) { + System.out.println(" " + er); + vr = vResponses.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" " + vr); + + System.out.println("\n6.2: getEntries(\"cs.no-counters\", \"cs.also-counters\") - getEntries but no subjects have counters."); + eResponses = counter.getEntries("cs.no-counters", "cs.also-counters"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { System.out.println(" " + er); @@ -156,35 +179,23 @@ public static void main(String[] args) throws Exception { System.out.println(" " + er); // ---------------------------------------------------------------------------------------------------- - System.out.println("\n8: getEntries(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - Get multiple when some subjects have counters."); - eResponses = counter.getEntries("no-counters", "cs.A", "cs.B", "cs.C"); + System.out.println("\n7.1: getMultiple(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - getMultiple when some subjects have counters."); + vResponses = counter.getMultiple("cs.no-counters", "cs.A", "cs.B", "cs.C"); + vr = vResponses.poll(1, TimeUnit.SECONDS); + while (vr != null && vr.isValue()) { + System.out.println(" " + vr); + vr = vResponses.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println(" " + vr + " -> No more values."); + + System.out.println("\n7.2: getEntries(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - getEntries when some subjects have counters."); + eResponses = counter.getEntries("cs.no-counters", "cs.A", "cs.B", "cs.C"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { System.out.println(" " + er); er = eResponses.poll(10, TimeUnit.MILLISECONDS); } System.out.println(" " + er + " -> No more entries."); - - // ---------------------------------------------------------------------------------------------------- - System.out.println("\n9: getOrElse() returns the 'else' value if the subject is not found."); - try { - counter.get("or-else"); - } - catch (JetStreamApiException e) { - System.out.println(" get(\"or-else\") -> " + e); - } - System.out.println(" getOrElse(\"or-else\", 77777) -> " + counter.getOrElse("or-else", 77777)); - - // ---------------------------------------------------------------------------------------------------- - System.out.println("\n10: It's safe to call set() even if the subject did not exist."); - try { - counter.get("cs.X"); - } - catch (JetStreamApiException e) { - System.out.println(" get(\"cs.X\") -> " + e); - } - System.out.println(" set(\"cs.X\", 99999) -> " + counter.add("cs.X", 99999)); - System.out.println(" get(\"cs.X\") -> " + counter.get("cs.X")); } } } diff --git a/counter/src/main/java/io/synadia/counter/Counter.java b/counter/src/main/java/io/synadia/counter/Counter.java index ce1ea8d..06f5af9 100644 --- a/counter/src/main/java/io/synadia/counter/Counter.java +++ b/counter/src/main/java/io/synadia/counter/Counter.java @@ -110,65 +110,47 @@ public BigInteger add(String subject, BigInteger value) throws JetStreamApiExcep return _add(subject, value.toString()); } - public BigInteger increment(String subject) throws JetStreamApiException, IOException { - return _add(subject, "1"); + public BigInteger setViaAdd(String subject, int value) throws JetStreamApiException, IOException { + return setViaAdd(subject, BigInteger.valueOf(value)); } - public BigInteger decrement(String subject) throws JetStreamApiException, IOException { - return _add(subject, "-1"); + public BigInteger setViaAdd(String subject, long value) throws JetStreamApiException, IOException { + return setViaAdd(subject, BigInteger.valueOf(value)); } - public BigInteger set(String subject, int value) throws JetStreamApiException, IOException { - return set(subject, BigInteger.valueOf(value)); - } - - public BigInteger set(String subject, long value) throws JetStreamApiException, IOException { - return set(subject, BigInteger.valueOf(value)); - } - - public BigInteger set(String subject, BigInteger value) throws JetStreamApiException, IOException { + public BigInteger setViaAdd(String subject, BigInteger value) throws JetStreamApiException, IOException { BigInteger bi = getOrElse(subject, BigInteger.ZERO); return _add(subject, value.subtract(bi).toString()); } - public BigInteger zero(String subject) throws JetStreamApiException, IOException { - return set(subject, BigInteger.ZERO); - } - public BigInteger get(String subject) throws JetStreamApiException, IOException { validateSingleSubject(subject); MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); - return new BigInteger(extractVal(mi.getData())); + return extractVal(mi); } - public BigInteger getOrElse(String subject, int dflt) { + public BigInteger getOrElse(String subject, int dflt) throws IOException { return getOrElse(subject, BigInteger.valueOf(dflt)); } - public BigInteger getOrElse(String subject, long dflt) { + public BigInteger getOrElse(String subject, long dflt) throws IOException { return getOrElse(subject, BigInteger.valueOf(dflt)); } - public BigInteger getOrElse(String subject, BigInteger dflt) { + public BigInteger getOrElse(String subject, BigInteger dflt) throws IOException { try { return get(subject); } - catch (IOException | JetStreamApiException e) { + catch (JetStreamApiException e) { return dflt; } } - public CounterValue getValue(String subject) throws JetStreamApiException, IOException { - validateSingleSubject(subject); - MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); - return new CounterValue(mi); + public LinkedBlockingQueue getMultiple(String... subjects) { + return getMultiple(Arrays.asList(subjects)); } - public LinkedBlockingQueue getValues(String... subjects) { - return getValues(Arrays.asList(subjects)); - } - - public LinkedBlockingQueue getValues(List subjects) { + public LinkedBlockingQueue getMultiple(List subjects) { LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects).noHeaders(); dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterValueResponse(mi))); @@ -176,6 +158,7 @@ public LinkedBlockingQueue getValues(List subjects } public CounterEntry getEntry(String subject) throws JetStreamApiException, IOException { + validateSingleSubject(subject); MessageInfo mi = jsm.getLastMessage(streamName, subject); return new CounterEntry(mi); } diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counter/src/main/java/io/synadia/counter/CounterEntry.java index b4180b7..836680b 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntry.java +++ b/counter/src/main/java/io/synadia/counter/CounterEntry.java @@ -31,12 +31,12 @@ public String getSubject() { @NonNull public BigInteger getValue() { - return new BigInteger(extractVal(mi.getData())); + return extractVal(mi); } @NonNull public BigInteger getLastIncrement() { - return new BigInteger(extractIncrement(mi.getHeaders())); + return extractIncrement(mi); } @NonNull diff --git a/counter/src/main/java/io/synadia/counter/CounterUtils.java b/counter/src/main/java/io/synadia/counter/CounterUtils.java index 2584697..7ef0895 100644 --- a/counter/src/main/java/io/synadia/counter/CounterUtils.java +++ b/counter/src/main/java/io/synadia/counter/CounterUtils.java @@ -3,23 +3,31 @@ package io.synadia.counter; +import io.nats.client.api.MessageInfo; import io.nats.client.impl.Headers; +import java.math.BigInteger; + public final class CounterUtils { public static final String INCREMENT_HEADER = "Nats-Incr"; - public static String extractVal(byte[] data) { - String s = new String(data); + public static BigInteger extractVal(MessageInfo mi) { + String s = new String(mi.getData()); // {"val":"-123"} // don't want to assume anything about how the json is formatted/spaced int colonAt = s.indexOf(':'); int numberStart = s.indexOf('"', colonAt + 1) + 1; int lastQuote = s.lastIndexOf('"'); - return s.substring(numberStart, lastQuote).trim(); + return new BigInteger(s.substring(numberStart, lastQuote).trim()); } - public static String extractIncrement(Headers h) { - return h == null ? "0" : h.getFirst(INCREMENT_HEADER); + public static BigInteger extractIncrement(MessageInfo mi) { + Headers h = mi.getHeaders(); + if (h == null) { + return BigInteger.ZERO; + } + String inc = h.getFirst(INCREMENT_HEADER); + return inc == null ? BigInteger.ZERO : new BigInteger(inc); } } diff --git a/counter/src/main/java/io/synadia/counter/CounterValue.java b/counter/src/main/java/io/synadia/counter/CounterValue.java deleted file mode 100644 index 474aefa..0000000 --- a/counter/src/main/java/io/synadia/counter/CounterValue.java +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. -// See LICENSE and NOTICE file for details. - -package io.synadia.counter; - -import io.nats.client.api.MessageInfo; -import io.nats.client.support.Status; -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; - -import java.math.BigInteger; - -import static io.synadia.counter.CounterUtils.extractVal; - -public class CounterValue { - public final String subject; - public final BigInteger value; - public final Status status; - - CounterValue(MessageInfo mi) { - this.status = mi.getStatus(); - if (mi.isMessage()) { - this.subject = mi.getSubject(); - this.value = new BigInteger(extractVal(mi.getData())); - } - else { - this.subject = ""; - this.value = BigInteger.ZERO; - } - } - - /** - * Whether this CounterValue is a regular value as opposed to an error/status - * @return true if the CounterEntry is a regular value - */ - public boolean isValue() { - return status == null; - } - - /** - * Whether this CounterEntry is a status message - * @return true if this CounterEntry is a status message - */ - public boolean isStatus() { - return status != null; - } - - /** - * Whether this CounterEntry is a status message and is a direct EOB status - * @return true if this CounterEntry is a status message and is a direct EOB status - */ - public boolean isEobStatus() { - return status != null && status.isEob(); - } - - /** - * Whether this CounterEntry is a status message and is an error status - * @return true if this CounterEntry is a status message and is an error status - */ - public boolean isErrorStatus() { - return status != null && !status.isEob(); - } - - @NonNull - public String getSubject() { - return subject; - } - - @NonNull - public BigInteger getValue() { - return value; - } - - @Nullable - public Status getStatus() { - return status; - } - - @Override - public String toString() { - if (isValue()) { - return "CounterValue{" + - "subject='" + subject + '\'' + - ", value=" + value + - '}'; - } - - return "CounterValue{" + - "status=" + status + - '}'; - } -} diff --git a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java index ab9e323..c152bc5 100644 --- a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java +++ b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java @@ -26,7 +26,7 @@ public boolean isValue() { @Nullable public BigInteger getValue() { - return mi.isMessage() ? new BigInteger(extractVal(mi.getData())) : null; + return mi.isMessage() ? extractVal(mi) : null; } @Override From 1f124675ff11080e20b96f558c953944d7e92402 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 29 Aug 2025 12:56:21 -0400 Subject: [PATCH 026/135] Counter unit tests --- .../examples/CounterContextExample.java | 2 + .../main/java/io/synadia/counter/Counter.java | 11 +- .../java/io/synadia/counter/CounterEntry.java | 57 +++-- .../java/io/synadia/counter/CounterUtils.java | 50 ++++- .../synadia/counter/CounterValueResponse.java | 2 +- .../java/io/synadia/counter/CounterTests.java | 207 ++++++++++++++++++ 6 files changed, 301 insertions(+), 28 deletions(-) create mode 100644 counter/src/test/java/io/synadia/counter/CounterTests.java diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index 315c169..b14bad5 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -7,6 +7,7 @@ import io.nats.client.JetStreamApiException; import io.nats.client.JetStreamManagement; import io.nats.client.Nats; +import io.nats.client.api.StorageType; import io.nats.client.api.StreamConfiguration; import io.synadia.counter.Counter; import io.synadia.counter.CounterEntryResponse; @@ -28,6 +29,7 @@ public static void main(String[] args) throws Exception { StreamConfiguration.builder() .name("counter-stream") .subjects("cs.*") + .storageType(StorageType.Memory) .build()); // ---------------------------------------------------------------------------------------------------- diff --git a/counter/src/main/java/io/synadia/counter/Counter.java b/counter/src/main/java/io/synadia/counter/Counter.java index 06f5af9..d0da300 100644 --- a/counter/src/main/java/io/synadia/counter/Counter.java +++ b/counter/src/main/java/io/synadia/counter/Counter.java @@ -88,6 +88,7 @@ private Counter(@NonNull String streamName, } private BigInteger _add(String subject, String sv) throws IOException, JetStreamApiException { + validateSingleSubject(subject); Headers h = new Headers(); h.put(INCREMENT_HEADER, sv); PublishAck pa = js.publish(subject, h, null); @@ -110,6 +111,14 @@ public BigInteger add(String subject, BigInteger value) throws JetStreamApiExcep return _add(subject, value.toString()); } + public BigInteger increment(String subject) throws JetStreamApiException, IOException { + return _add(subject, "1"); + } + + public BigInteger decrement(String subject) throws JetStreamApiException, IOException { + return _add(subject, "-1"); + } + public BigInteger setViaAdd(String subject, int value) throws JetStreamApiException, IOException { return setViaAdd(subject, BigInteger.valueOf(value)); } @@ -126,7 +135,7 @@ public BigInteger setViaAdd(String subject, BigInteger value) throws JetStreamAp public BigInteger get(String subject) throws JetStreamApiException, IOException { validateSingleSubject(subject); MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); - return extractVal(mi); + return extractVal(mi.getData()); } public BigInteger getOrElse(String subject, int dflt) throws IOException { diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counter/src/main/java/io/synadia/counter/CounterEntry.java index 836680b..bd4804b 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntry.java +++ b/counter/src/main/java/io/synadia/counter/CounterEntry.java @@ -4,53 +4,78 @@ package io.synadia.counter; import io.nats.client.api.MessageInfo; +import io.nats.client.impl.Headers; import org.jspecify.annotations.NonNull; import java.math.BigInteger; -import java.util.HashMap; import java.util.Map; -import static io.synadia.counter.CounterUtils.extractIncrement; -import static io.synadia.counter.CounterUtils.extractVal; +import static io.synadia.counter.CounterUtils.*; public class CounterEntry { - public final MessageInfo mi; + private final String subject; + private final BigInteger value; + private final BigInteger lastIncrement; + private final Map> sources; CounterEntry(MessageInfo mi) { if (!mi.isMessage()) { - throw new IllegalArgumentException("Message is not a counter message"); + throw invalidCounterMessage(null); } - this.mi = mi; + + subject = mi.getSubject(); + try { + value = extractVal(mi.getData()); + } + catch (RuntimeException e) { + throw invalidCounterMessage(e); + } + + Headers h = mi.getHeaders(); + if (h == null) { + throw invalidCounterMessage(null); + } + + String temp = h.getFirst(CounterUtils.INCREMENT_HEADER); + if (temp == null) { + throw invalidCounterMessage(null); + } + lastIncrement = extractLastIncrement(temp); + + sources = extractSources(h.getFirst(CounterUtils.SOURCES_HEADER)); + } + + private static RuntimeException invalidCounterMessage(Exception e) { + return new RuntimeException("Message is not a counter message", e); } @NonNull public String getSubject() { - //noinspection DataFlowIssue we know it's not null - return mi.getSubject(); + return subject; } @NonNull public BigInteger getValue() { - return extractVal(mi); + return value; } @NonNull public BigInteger getLastIncrement() { - return extractIncrement(mi); + return lastIncrement; } @NonNull - public Map getSources() { - return new HashMap<>(); // TODO + public Map> getSources() { + return sources; } @Override public String toString() { return "CounterEntry{" + - "subject=\"" + getSubject() + '\"' + - ", value=" + getValue() + - ", lastIncrement=" + getLastIncrement() + - ", sources=" + getSources() + + "subject=\"" + subject + '\"' + + ", value=" + value + + ", lastIncrement=" + lastIncrement + + ", sources=" + sources + '}'; } } diff --git a/counter/src/main/java/io/synadia/counter/CounterUtils.java b/counter/src/main/java/io/synadia/counter/CounterUtils.java index 7ef0895..1db9b07 100644 --- a/counter/src/main/java/io/synadia/counter/CounterUtils.java +++ b/counter/src/main/java/io/synadia/counter/CounterUtils.java @@ -3,17 +3,24 @@ package io.synadia.counter; -import io.nats.client.api.MessageInfo; -import io.nats.client.impl.Headers; +import io.nats.client.support.JsonParseException; +import io.nats.client.support.JsonParser; +import io.nats.client.support.JsonValue; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; public final class CounterUtils { public static final String INCREMENT_HEADER = "Nats-Incr"; + public static final String SOURCES_HEADER = "Nats-Counter-Sources"; - public static BigInteger extractVal(MessageInfo mi) { - String s = new String(mi.getData()); + public static BigInteger extractVal(byte @NonNull [] valBytes) { + String s = new String(valBytes); + // it's just faster to parse this manually // {"val":"-123"} // don't want to assume anything about how the json is formatted/spaced int colonAt = s.indexOf(':'); @@ -22,12 +29,35 @@ public static BigInteger extractVal(MessageInfo mi) { return new BigInteger(s.substring(numberStart, lastQuote).trim()); } - public static BigInteger extractIncrement(MessageInfo mi) { - Headers h = mi.getHeaders(); - if (h == null) { - return BigInteger.ZERO; + public static BigInteger extractLastIncrement(@NonNull String numberString) { + return new BigInteger(numberString); + } + + public static Map> extractSources(@Nullable String json) { + Map> sources = new HashMap<>(); + if (json != null) { + try { + JsonValue v = JsonParser.parse(json); + if (v.map != null) { + for (Map.Entry entry : v.map.entrySet()) { + String key = entry.getKey(); + Map value = entry.getValue().map; + if (value != null) { + Map sourceValue = new HashMap<>(); + sources.put(key, sourceValue); + for (Map.Entry entry2 : value.entrySet()) { + String key2 = entry2.getKey(); + JsonValue value2 = entry2.getValue(); + sourceValue.put(key2, new BigInteger(value2.string)); + } + } + } + } + } + catch (JsonParseException e) { + throw new RuntimeException(e); + } } - String inc = h.getFirst(INCREMENT_HEADER); - return inc == null ? BigInteger.ZERO : new BigInteger(inc); + return sources; } } diff --git a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java index c152bc5..6e40d85 100644 --- a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java +++ b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java @@ -26,7 +26,7 @@ public boolean isValue() { @Nullable public BigInteger getValue() { - return mi.isMessage() ? extractVal(mi) : null; + return mi.isMessage() ? extractVal(mi.getData()) : null; } @Override diff --git a/counter/src/test/java/io/synadia/counter/CounterTests.java b/counter/src/test/java/io/synadia/counter/CounterTests.java new file mode 100644 index 0000000..e093e69 --- /dev/null +++ b/counter/src/test/java/io/synadia/counter/CounterTests.java @@ -0,0 +1,207 @@ +package io.synadia.counter; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamConfiguration; +import nats.io.NatsServerRunner; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +import static io.synadia.counter.CounterUtils.extractSources; +import static io.synadia.counter.CounterUtils.extractVal; +import static org.junit.jupiter.api.Assertions.*; + +public class CounterTests { + static NatsServerRunner runner; + static Connection nc; + static JetStreamManagement jsm; + static JetStream js; + + @BeforeAll + public static void beforeAll() throws Exception { + NatsServerRunner.setDefaultOutputLevel(Level.WARNING); + runner = new NatsServerRunner(false, true); + Options options = Options.builder() + .server(runner.getURI()) + .errorListener(new ErrorListener() {}) + .build(); + nc = Nats.connect(options); + jsm = nc.jetStreamManagement(); + js = nc.jetStream(); + } + + @AfterAll + public static void afterAll() throws Exception { + runner.close(); + } + + private static Counter createCounterStream(String streamName, String... subjects) throws JetStreamApiException, IOException { + return Counter.createCounterStream(nc, + StreamConfiguration.builder() + .name(streamName) + .subjects(subjects) + .storageType(StorageType.Memory) + .build()); + } + + @Test + public void testCounterExceptions() { + String streamName = NUID.nextGlobalSequence(); + String subjectPrefix = NUID.nextGlobalSequence(); + String wild = subjectPrefix + ".*"; + try { + Counter counter = createCounterStream(streamName, wild); + assertThrows(IllegalArgumentException.class, () -> counter.add(wild, 1)); + assertThrows(IllegalArgumentException.class, () -> counter.get(wild)); + + String streamNameX = NUID.nextGlobalSequence(); + String subjectPrefixX = NUID.nextGlobalSequence(); + String wildX = subjectPrefixX + ".*"; + jsm.addStream(StreamConfiguration.builder() + .name(streamNameX) + .subjects(wildX) + .storageType(StorageType.Memory) + .build()); + assertThrows(IllegalArgumentException.class, () -> new Counter(streamNameX, nc)); + } + catch (JetStreamApiException | IOException e) { + fail(e.getMessage()); + } + } + + @Test + public void testCounterBasics() throws Exception { + String streamName = NUID.nextGlobalSequence(); + String subjectPrefix = NUID.nextGlobalSequence(); + Counter counter = createCounterStream(streamName, subjectPrefix + ".*"); + + String subject1 = subjectPrefix + "." + NUID.nextGlobalSequence(); + String subject2 = subjectPrefix + "." + NUID.nextGlobalSequence(); + String subject3 = subjectPrefix + "." + NUID.nextGlobalSequence(); + + assertEquals(1, counter.add(subject1, 1).intValue()); + assertEquals(1, counter.get(subject1).intValue()); + assertEquals(3, counter.add(subject1, 2).intValue()); + assertEquals(3, counter.get(subject1).intValue()); + assertEquals(6, counter.add(subject1, 3).intValue()); + assertEquals(6, counter.get(subject1).intValue()); + assertEquals(5, counter.add(subject1, -1).intValue()); + assertEquals(5, counter.get(subject1).intValue()); + assertEquals(6, counter.increment(subject1).intValue()); + assertEquals(5, counter.decrement(subject1).intValue()); + + assertEquals(5, counter.getOrElse(subject1, 99).intValue()); + assertEquals(Integer.MAX_VALUE, counter.getOrElse("not-exist", Integer.MAX_VALUE).intValue()); + assertEquals(Long.MAX_VALUE, counter.getOrElse("not-exist", Long.MAX_VALUE).longValue()); + + assertEquals(-1, counter.add(subject2, -1).intValue()); + assertEquals(-1, counter.get(subject2).intValue()); + assertEquals(-3, counter.add(subject2, -2).intValue()); + assertEquals(-3, counter.get(subject2).intValue()); + assertEquals(-6, counter.add(subject2, -3).intValue()); + assertEquals(-6, counter.get(subject2).intValue()); + assertEquals(-5, counter.add(subject2, 1).intValue()); + assertEquals(-5, counter.get(subject2).intValue()); + + assertEquals(Integer.MAX_VALUE, counter.setViaAdd(subject3, Integer.MAX_VALUE).intValue()); + assertEquals(Integer.MAX_VALUE, counter.get(subject3).intValue()); + assertEquals(Long.MAX_VALUE, counter.setViaAdd(subject3, Long.MAX_VALUE).longValue()); + assertEquals(Long.MAX_VALUE, counter.get(subject3).longValue()); + + assertEquals(10, counter.setViaAdd(subject1, 10).intValue()); + assertEquals(100, counter.setViaAdd(subject2, 100).intValue()); + assertEquals(1000, counter.setViaAdd(subject3, 1000).intValue()); + assertEquals(10, counter.get(subject1).intValue()); + assertEquals(100, counter.get(subject2).intValue()); + assertEquals(1000, counter.get(subject3).intValue()); + + BigInteger total = BigInteger.ZERO; + LinkedBlockingQueue vResponses = counter.getMultiple(subject1, subject2, subject3); + CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); + while (vr != null && vr.isValue()) { + total = total.add(vr.getValue()); + vr = vResponses.poll(10, TimeUnit.MILLISECONDS); + } + assertNotNull(vr); + assertTrue(vr.isEobStatus()); + assertEquals(1110, total.intValue()); + + total = BigInteger.ZERO; + LinkedBlockingQueue eResponses = counter.getEntries(subject1, subject2, subject3); + CounterEntryResponse er = eResponses.poll(1, TimeUnit.SECONDS); + while (er != null && er.isEntry()) { + CounterEntry entry = er.getEntry(); + assertNotNull(entry); + total = total.add(entry.getValue()); + er = eResponses.poll(10, TimeUnit.MILLISECONDS); + } + assertNotNull(er); + assertTrue(er.isEobStatus()); + assertEquals(1110, total.intValue()); + + total = BigInteger.ZERO; + vResponses = counter.getMultiple(subjectPrefix + ".*"); + vr = vResponses.poll(1, TimeUnit.SECONDS); + while (vr != null && vr.isValue()) { + total = total.add(vr.getValue()); + vr = vResponses.poll(10, TimeUnit.MILLISECONDS); + } + assertNotNull(vr); + assertTrue(vr.isEobStatus()); + assertEquals(1110, total.intValue()); + + total = BigInteger.ZERO; + eResponses = counter.getEntries(subjectPrefix + ".*"); + er = eResponses.poll(1, TimeUnit.SECONDS); + while (er != null && er.isEntry()) { + CounterEntry entry = er.getEntry(); + assertNotNull(entry); + total = total.add(entry.getValue()); + er = eResponses.poll(10, TimeUnit.MILLISECONDS); + } + assertNotNull(er); + assertTrue(er.isEobStatus()); + assertEquals(1110, total.intValue()); + } + + @Test + public void testExtractVal() { + assertEquals(10, extractVal("{\"val\":\"10\"}".getBytes()).intValue()); + assertEquals(0, extractVal("{\"val\":\"0\"}".getBytes()).intValue()); + assertEquals(-10, extractVal("{\"val\":\"-10\"}".getBytes()).intValue()); + String l = "" + Long.MAX_VALUE; + assertEquals(Long.MAX_VALUE, extractVal(("{\"val\":\"" + l + "\"}").getBytes()).longValue()); + } + + @Test + public void testExtractSources() { + Map> map = extractSources(null); + assertNotNull(map); + assertTrue(map.isEmpty()); + + map = extractSources(SOURCES_JSON); + assertNotNull(map); + assertFalse(map.isEmpty()); + assertEquals(2, map.size()); + + Map map2 = map.get("source1"); + assertNotNull(map2); + assertFalse(map2.isEmpty()); + assertEquals(1, map2.size()); + + BigInteger value = map2.get("subject1"); + assertNotNull(value); + assertInstanceOf(BigInteger.class, value); + assertEquals(10, value.longValue()); + } + + private final static String SOURCES_JSON = "{\"source1\":{\"subject1\":\"10\"},\"source2\":{\"subject2\":\"20\",\"subject3\":\"90\"}}"; +} From 659675773bde4e56860d797d3775d1f65614b319 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 15 Sep 2025 12:11:06 -0400 Subject: [PATCH 027/135] Fix direct message test based on 2.12 functionality --- .github/workflows/sm-main.yml | 47 +++++++++++++++++++ .github/workflows/sm-pr.yml | 41 ++++++++++++++++ .github/workflows/sm-release.yml | 39 +++++++++++++++ counter/README.md | 2 +- counter/build.gradle | 4 +- counter/src/main/javadoc/overview.html | 2 +- .../io/synadia/direct/DirectBatchTests.java | 24 +++++----- encoded-kv/src/main/javadoc/overview.html | 2 +- .../src/main/javadoc/overview.html | 2 +- request-many/README.md | 6 +-- request-many/src/main/javadoc/overview.html | 2 +- retrier/src/main/javadoc/overview.html | 2 +- 12 files changed, 150 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/sm-main.yml create mode 100644 .github/workflows/sm-pr.yml create mode 100644 .github/workflows/sm-release.yml diff --git a/.github/workflows/sm-main.yml b/.github/workflows/sm-main.yml new file mode 100644 index 0000000..713ddbc --- /dev/null +++ b/.github/workflows/sm-main.yml @@ -0,0 +1,47 @@ +name: Chaos Runner Main Snapshot + +on: + push: + branches: + - main + paths: + - 'schedule-message/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd schedule-message + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd schedule-message + ./gradlew javadoc + popd + - name: Publish Snapshot + run: | + pushd schedule-message + ./gradlew -i publishToSonatype + popd diff --git a/.github/workflows/sm-pr.yml b/.github/workflows/sm-pr.yml new file mode 100644 index 0000000..60e8da3 --- /dev/null +++ b/.github/workflows/sm-pr.yml @@ -0,0 +1,41 @@ +name: Chaos Runner Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'schedule-message/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd schedule-message + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd schedule-message + ./gradlew javadoc + popd diff --git a/.github/workflows/sm-release.yml b/.github/workflows/sm-release.yml new file mode 100644 index 0000000..94ccac2 --- /dev/null +++ b/.github/workflows/sm-release.yml @@ -0,0 +1,39 @@ +name: Chaos Runner Release + +on: + push: + tags: [ 'sm/*' ] + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: "release" + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd schedule-message + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify, Sign and Publish Release + run: | + pushd schedule-message + ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + popd diff --git a/counter/README.md b/counter/README.md index fb6ac8b..f7dd5e1 100644 --- a/counter/README.md +++ b/counter/README.md @@ -1,6 +1,6 @@ ![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) -# JetStream Distributed Counter CRDT +# JetStream Distributed Counters CRDT https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md diff --git a/counter/build.gradle b/counter/build.gradle index 3d92b16..dec22f5 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -75,7 +75,7 @@ jar { manifest { attributes('Automatic-Module-Name': 'io.synadia.counter') } - bnd (['Implementation-Title': 'JetStream Distributed Counter', + bnd (['Implementation-Title': 'JetStream Distributed Counters', 'Implementation-Version': jarVersion, 'Implementation-Vendor': 'synadia.io'] ) @@ -90,7 +90,7 @@ test { javadoc { options.overview = 'src/main/javadoc/overview.html' // relative to source root source = sourceSets.main.allJava - title = "Synadia Communications Inc. JetStream Distributed Counter" + title = "Synadia Communications Inc. JetStream Distributed Counters" classpath = sourceSets.main.runtimeClasspath doLast { if (!OperatingSystem.current().isWindows()) { diff --git a/counter/src/main/javadoc/overview.html b/counter/src/main/javadoc/overview.html index 32fb10c..659b20f 100644 --- a/counter/src/main/javadoc/overview.html +++ b/counter/src/main/javadoc/overview.html @@ -1,5 +1,5 @@ diff --git a/direct-batch/src/test/java/io/synadia/direct/DirectBatchTests.java b/direct-batch/src/test/java/io/synadia/direct/DirectBatchTests.java index 12efb56..21b92f0 100644 --- a/direct-batch/src/test/java/io/synadia/direct/DirectBatchTests.java +++ b/direct-batch/src/test/java/io/synadia/direct/DirectBatchTests.java @@ -420,18 +420,18 @@ private static void doQueue(DirectBatchContext db, MessageBatchGetRequest mbgr, @SuppressWarnings("DuplicateBranchesInSwitch") private static void _verify(List list, String label, boolean lastIsEob) { switch (label) { - case "1" : _verify(list, 1, 23, lastIsEob, 1, 2, 3); break; - case "1A" : _verify(list, 1, 3, lastIsEob, 1, 6, 11); break; - case "2" : _verify(list, 4, 20, lastIsEob, 4, 5, 6); break; - case "2A" : _verify(list, 6, 2, lastIsEob, 6, 11, 16); break; - case "3" : _verify(list, 6, 18, lastIsEob, 6, 7, 8); break; - case "3A" : _verify(list, 6, 2, lastIsEob, 6, 11, 16); break; - case "4" : _verify(list, 1, 23, lastIsEob, 1, 2); break; - case "4A" : _verify(list, 1, 3, lastIsEob, 1, 6); break; - case "5" : _verify(list, 4, 20, lastIsEob, 4, 5); break; - case "5A" : _verify(list, 6, 2, lastIsEob, 6, 11); break; - case "6" : _verify(list, 6, 18, lastIsEob, 6, 7); break; - case "6A" : _verify(list, 6, 2, lastIsEob, 6, 11); break; + case "1" : _verify(list, 1, 22, lastIsEob, 1, 2, 3); break; + case "1A" : _verify(list, 1, 2, lastIsEob, 1, 6, 11); break; + case "2" : _verify(list, 4, 19, lastIsEob, 4, 5, 6); break; + case "2A" : _verify(list, 6, 1, lastIsEob, 6, 11, 16); break; + case "3" : _verify(list, 6, 17, lastIsEob, 6, 7, 8); break; + case "3A" : _verify(list, 6, 1, lastIsEob, 6, 11, 16); break; + case "4" : _verify(list, 1, 22, lastIsEob, 1, 2); break; + case "4A" : _verify(list, 1, 2, lastIsEob, 1, 6); break; + case "5" : _verify(list, 4, 19, lastIsEob, 4, 5); break; + case "5A" : _verify(list, 6, 1, lastIsEob, 6, 11); break; + case "6" : _verify(list, 6, 17, lastIsEob, 6, 7); break; + case "6A" : _verify(list, 6, 1, lastIsEob, 6, 11); break; case "M1" : _verify(list, 21, 0, lastIsEob, 21, 23, 25); break; case "M1A" : _verify(list, 21, 2, lastIsEob, 21, 22, 23, 24, 25); break; case "M2" : _verify(list, 18, 0, lastIsEob, 18, 20, 21); break; diff --git a/encoded-kv/src/main/javadoc/overview.html b/encoded-kv/src/main/javadoc/overview.html index 4cb8395..201bd87 100644 --- a/encoded-kv/src/main/javadoc/overview.html +++ b/encoded-kv/src/main/javadoc/overview.html @@ -1,5 +1,5 @@ diff --git a/js-publish-extensions/src/main/javadoc/overview.html b/js-publish-extensions/src/main/javadoc/overview.html index de17c46..9426c9d 100644 --- a/js-publish-extensions/src/main/javadoc/overview.html +++ b/js-publish-extensions/src/main/javadoc/overview.html @@ -1,5 +1,5 @@ diff --git a/request-many/README.md b/request-many/README.md index f1f9d9a..4071390 100644 --- a/request-many/README.md +++ b/request-many/README.md @@ -14,10 +14,10 @@ instead of the usual first response for a request. This allows you to implement   **Gradle and Maven** `io.synadia:request-many` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:retrier-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:request--many-00BC8E?labelColor=grey&style=flat) [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier) -[![javadoc](https://javadoc.io/badge2/io.synadia/retrier/javadoc.svg)](https://javadoc.io/doc/io.synadia/retrier) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/request-many/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/request-many) +[![javadoc](https://javadoc.io/badge2/io.synadia/request-many/javadoc.svg)](https://javadoc.io/doc/io.synadia/request-many) ### Request Many Usage diff --git a/request-many/src/main/javadoc/overview.html b/request-many/src/main/javadoc/overview.html index c14e2f6..f42bdc7 100644 --- a/request-many/src/main/javadoc/overview.html +++ b/request-many/src/main/javadoc/overview.html @@ -1,5 +1,5 @@ diff --git a/retrier/src/main/javadoc/overview.html b/retrier/src/main/javadoc/overview.html index cff357e..0a0f7ab 100644 --- a/retrier/src/main/javadoc/overview.html +++ b/retrier/src/main/javadoc/overview.html @@ -1,5 +1,5 @@ From f7dfdf374c211768f9cdec8bf8052e3b6ac1306d Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 15 Sep 2025 15:53:50 -0400 Subject: [PATCH 028/135] update jnats dependency --- chaos-runner/build.gradle | 2 +- counter/build.gradle | 2 +- direct-batch/build.gradle | 2 +- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 1eb686a..28dda4c 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' // this is only for the example and the uber jar won't include it - implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/counter/build.gradle b/counter/build.gradle index dec22f5..25cb970 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' implementation 'io.synadia:direct-batch:0.1.3-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 346c497..1e19c4c 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index cb5376a..93cfa46 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index 033c011..2bfaf68 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index ba1fc02..aec0f31 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index e1be3ce..23b8239 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' From b46193600bbf00bca546cc86042a48085f3f8ebb Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 15 Sep 2025 15:54:22 -0400 Subject: [PATCH 029/135] Schedule Message --- schedule-message/.gitignore | 77 ++++++ schedule-message/LICENSE | 201 ++++++++++++++++ schedule-message/NOTICE | 5 + schedule-message/README.md | 20 ++ schedule-message/build.gradle | 210 +++++++++++++++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + schedule-message/gradlew | 183 +++++++++++++++ schedule-message/gradlew.bat | 103 ++++++++ schedule-message/settings.gradle | 10 + .../examples/ScheduleAtSpecificTime.java | 61 +++++ .../examples/ScheduleExampleUtils.java | 48 ++++ .../synadia/sm/ScheduledMessageBuilder.java | 220 ++++++++++++++++++ .../src/main/javadoc/images/favicon.ico | Bin 0 -> 1150 bytes .../src/main/javadoc/images/large-logo.png | Bin 0 -> 6533 bytes .../src/main/javadoc/images/synadia-logo.png | Bin 0 -> 19014 bytes .../src/main/javadoc/overview.html | 13 ++ .../src/main/resources/placeholder.txt | 1 + .../src/test/resources/placeholder.txt | 1 + schedule-message/test.bat | 5 + 20 files changed, 1163 insertions(+) create mode 100644 schedule-message/.gitignore create mode 100644 schedule-message/LICENSE create mode 100644 schedule-message/NOTICE create mode 100644 schedule-message/README.md create mode 100644 schedule-message/build.gradle create mode 100644 schedule-message/gradle/wrapper/gradle-wrapper.jar create mode 100644 schedule-message/gradle/wrapper/gradle-wrapper.properties create mode 100644 schedule-message/gradlew create mode 100644 schedule-message/gradlew.bat create mode 100644 schedule-message/settings.gradle create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java create mode 100644 schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java create mode 100644 schedule-message/src/main/javadoc/images/favicon.ico create mode 100644 schedule-message/src/main/javadoc/images/large-logo.png create mode 100644 schedule-message/src/main/javadoc/images/synadia-logo.png create mode 100644 schedule-message/src/main/javadoc/overview.html create mode 100644 schedule-message/src/main/resources/placeholder.txt create mode 100644 schedule-message/src/test/resources/placeholder.txt create mode 100644 schedule-message/test.bat diff --git a/schedule-message/.gitignore b/schedule-message/.gitignore new file mode 100644 index 0000000..b3e2ca5 --- /dev/null +++ b/schedule-message/.gitignore @@ -0,0 +1,77 @@ + +# NATS stuff # +############## +gnatsd.log +*.csv + +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +/bin + +# Packages # +############ +*.7z +*.dmg +*.gz +*.iso +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log + +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db + +# Editor Files # +################ +*~ +*.swp +.sts4-cache/* + +# Gradle Files # +################ +.gradle +.m2 + +# Build output directies +/target +*/target +/build +*/build + +# IntelliJ specific files/directories +out +.idea +*.ipr +*.iws +*.iml +atlassian-ide-plugin.xml + +# Eclipse specific files/directories +.classpath +.project +.settings +.metadata + +# NetBeans specific files/directories +.nbattrs + +# VSCode +.vscode/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/target/ diff --git a/schedule-message/LICENSE b/schedule-message/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/schedule-message/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/schedule-message/NOTICE b/schedule-message/NOTICE new file mode 100644 index 0000000..ff3c8b4 --- /dev/null +++ b/schedule-message/NOTICE @@ -0,0 +1,5 @@ +Orbit Java +Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. + +This product includes software developed at +Synadia Communications Inc. \ No newline at end of file diff --git a/schedule-message/README.md b/schedule-message/README.md new file mode 100644 index 0000000..2f943c2 --- /dev/null +++ b/schedule-message/README.md @@ -0,0 +1,20 @@ +![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) + +# JetStream Scheduled Message + +https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md + + +**Current Release**: N/A +  **Current Snapshot**: 0.1.0-SNAPSHOT +  **Gradle and Maven** `io.synadia:scheduled-message` +[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) + +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) +[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) +[![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) + +--- +Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle new file mode 100644 index 0000000..0888169 --- /dev/null +++ b/schedule-message/build.gradle @@ -0,0 +1,210 @@ +import aQute.bnd.gradle.Bundle +import org.gradle.internal.os.OperatingSystem + +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'jacoco' + id 'com.github.kt3k.coveralls' version '2.12.0' + id 'biz.aQute.bnd.builder' version '5.1.2' + id "org.gradle.test-retry" version "1.1.9" + id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' + id 'signing' +} + +def jarVersion = "0.0.1" +group = 'io.synadia' + +def isMerge = System.getenv("BUILD_EVENT") == "push" +def isRelease = System.getenv("BUILD_EVENT") == "release" + +// version is the variable the build actually uses. +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() + maven { + url "https://repo1.maven.org/maven2/" + } + maven { + url "https://central.sonatype.com/repository/maven-snapshots/" + } +} + +dependencies { + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'org.jspecify:jspecify:1.0.0' + + testImplementation 'commons-codec:commons-codec:1.18.0' + testImplementation 'io.nats:jnats-server-runner:1.2.8' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' +} + +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' +} + +sourceSets { + main { + java { + srcDirs = ['src/main/java','src/examples/java'] + } + } + test { + java { + srcDirs = ['src/test/java'] + } + } +} + +tasks.register('bundle', Bundle) { + from sourceSets.main.output + exclude("io/synadia/examples/**") +} + +jar { + manifest { + attributes('Automatic-Module-Name': 'io.synadia.schedule-message') + } + bnd (['Implementation-Title': 'JetStream Scheduled Messages', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io'] + ) + exclude("io/synadia/examples/**") +} + +test { + // Use junit platform for unit tests + useJUnitPlatform() +} + +javadoc { + options.overview = 'src/main/javadoc/overview.html' // relative to source root + source = sourceSets.main.allJava + title = "Synadia Communications Inc. JetStream Scheduled Messages" + classpath = sourceSets.main.runtimeClasspath + doLast { + if (!OperatingSystem.current().isWindows()) { + exec { + println "Updating favicon on all html files" + workingDir 'build/docs/javadoc' + // Only on linux, mac at this point + commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' + } + copy { + println "Copying images to javadoc folder" + from 'src/main/javadoc/images' + into 'build/docs/javadoc' + } + } + } +} + +tasks.register('examplesJar', Jar) { + archiveClassifier.set('examples') + manifest { + attributes('Implementation-Title': 'JetStream Scheduled Messages', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io') + } + from(sourceSets.main.output) { + include "io/synadia/examples/**" + } +} + +tasks.register('javadocJar', Jar) { + archiveClassifier.set('javadoc') + from javadoc +} + +tasks.register('sourcesJar', Jar) { + archiveClassifier.set('sources') + from sourceSets.main.allSource +} + +jacoco { + toolVersion = "0.8.6" +} + +jacocoTestReport { + reports { + xml.enabled = true // coveralls plugin depends on xml format report + html.enabled = true + } + afterEvaluate { // only report on main library not examples + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, + exclude: ['**/examples**']) + })) + } +} + +artifacts { + archives javadocJar, sourcesJar, examplesJar +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username = System.getenv('OSSRH_USERNAME') + password = System.getenv('OSSRH_PASSWORD') + } + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact examplesJar + artifact javadocJar + pom { + name = 'schedule-message' + packaging = 'jar' + groupId = group + artifactId = archivesBaseName + description = 'Synadia Communications Inc. JetStream Scheduled Messages' + url = 'https://github.com/synadia-io/orbit.java' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = "synadia" + name = "Synadia" + email = "info@synadia.com" + url = "https://synadia.io" + } + } + scm { + url = 'https://github.com/synadia-io/orbit.java' + } + } + } + } +} + +if (isRelease) { + signing { + def signingKeyId = System.getenv('SIGNING_KEY_ID') + def signingKey = System.getenv('SIGNING_KEY') + def signingPassword = System.getenv('SIGNING_PASSWORD') + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + sign configurations.archives + sign publishing.publications.mavenJava + } +} diff --git a/schedule-message/gradle/wrapper/gradle-wrapper.jar b/schedule-message/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 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 literal 0 HcmV?d00001 diff --git a/schedule-message/gradle/wrapper/gradle-wrapper.properties b/schedule-message/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..442d913 --- /dev/null +++ b/schedule-message/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/schedule-message/gradlew b/schedule-message/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/schedule-message/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/schedule-message/gradlew.bat b/schedule-message/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/schedule-message/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/schedule-message/settings.gradle b/schedule-message/settings.gradle new file mode 100644 index 0000000..df380d0 --- /dev/null +++ b/schedule-message/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'schedule-message' diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java new file mode 100644 index 0000000..6d81a4e --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java @@ -0,0 +1,61 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.support.DateTimeUtils; +import io.nats.client.support.Debug; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.util.concurrent.CountDownLatch; + +public class ScheduleAtSpecificTime { + public static final String STREAM = "scheduler-stream"; + public static final String SCHEDULER_SUBJECT = "scheduler-subject"; + public static final String TARGET_SUBJECT = "target-subject"; + + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connectReconnectOnConnect(options)) { + ScheduleExampleUtils.createOrReplaceStream(connection, STREAM, SCHEDULER_SUBJECT, TARGET_SUBJECT); + JetStream js = connection.jetStream(); + + CountDownLatch latch = new CountDownLatch(2); + Dispatcher d = connection.createDispatcher(); + + // subscribe to the subject that receives the schedule message + js.subscribe(SCHEDULER_SUBJECT, d, m -> { + Debug.info("SCHEDULE", m); + m.ack(); + }, false); + + // subscribe to the target subject + js.subscribe(TARGET_SUBJECT, d, m -> { + Debug.info("TARGET", m); + m.ack(); + latch.countDown(); + }, false); + + Message m = new ScheduledMessageBuilder() + .publishSubject(SCHEDULER_SUBJECT) + .targetSubject(TARGET_SUBJECT) + .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(3)) + .data("payload") + .build(); + Debug.info("PUBLISH", m); + js.publish(m); + + latch.await(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java new file mode 100644 index 0000000..6109a69 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java @@ -0,0 +1,48 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamConfiguration; +import io.nats.client.api.StreamInfo; +import io.nats.client.support.Debug; + +import java.io.IOException; + +public class ScheduleExampleUtils { + + public static void createOrReplaceStream(Connection connection, String stream, String... subjects) throws IOException, JetStreamApiException { + createOrReplaceStream(connection.jetStreamManagement(), stream, subjects); + } + + public static void createOrReplaceStream(JetStreamManagement jsm, String stream, String... subjects) throws IOException, JetStreamApiException { + report("createOrReplaceStream"); + try { + jsm.deleteStream(stream); + } + catch (Exception ignore) {} + + try { + StreamConfiguration sc = StreamConfiguration.builder() + .name(stream) + .storageType(StorageType.Memory) + .subjects(subjects) + .allowMessageSchedules() + .build(); + StreamInfo si = jsm.addStream(sc); + Debug.info("Created stream: " + si.getConfiguration()); + } + catch (Exception e) { + Debug.info("Failed creating stream: '' " + e); + System.exit(-1); + } + } + + public static void report(String... strings) { + System.out.println("[" + System.currentTimeMillis() + "] " + String.join(" ", strings)); + } +} diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java new file mode 100644 index 0000000..a1332cf --- /dev/null +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -0,0 +1,220 @@ +package io.synadia.sm; + +import io.nats.client.Message; +import io.nats.client.MessageTtl; +import io.nats.client.impl.Headers; +import io.nats.client.impl.NatsMessage; +import io.nats.client.support.DateTimeUtils; +import io.nats.client.support.Validator; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; + +/** + * Class to make setting a per message ttl easier. + */ +public class ScheduledMessageBuilder { + + public enum Predefined { + /** + * Run once a year, midnight, Jan. 1st. Same as Yearly. Equivalent to cron string 0 0 0 1 1 * + */ + Annually("@annually"), + + /** + * Run once a year, midnight, Jan. 1st. Same as Annually. Equivalent to cron string 0 0 0 1 1 * + */ + Yearly("@yearly"), + + /** + * Run once a month, midnight, first of month. Same as cron format 0 0 0 1 * * + */ + Monthly("@monthly"), + + /** + * Run once a week, midnight between Sat/Sun. Equivalent to cron string 0 0 0 * * 0 + */ + Weekly("@weekly"), + + /** + * Run once a day, midnight. Same as Daily. Equivalent to cron string 0 0 0 * * * + */ + Midnight("@midnight"), + + /** + * Run once a day, midnight. Same as Midnight. Equivalent to cron string 0 0 0 * * * + */ + Daily("@daily"), + + /** + * Run once an hour, beginning of hour. Equivalent to cron string 0 0 * * * * + */ + Hourly("@hourly"); + + final String value; + + Predefined(String value) { + this.value = value; + } + } + + private String scheduleString; + private String publishSubject; + private String targetSubject; + private Headers headers; + private byte[] data; + private MessageTtl messageTtl; + + public ScheduledMessageBuilder() { + } + + public ScheduledMessageBuilder(Message message) { + this.publishSubject = message.getSubject(); + this.headers = message.getHeaders(); + this.data = message.getData(); + } + + /** + * Set the publish subject + * @param publishSubject the publish subject + * @return the builder + */ + public ScheduledMessageBuilder publishSubject(final String publishSubject) { + this.publishSubject = publishSubject; + return this; + } + + /** + * Set the target subject + * @param targetSubject the target subject + * @return the builder + */ + public ScheduledMessageBuilder targetSubject(final String targetSubject) { + this.targetSubject = targetSubject; + return this; + } + + /** + * Set the headers + * @param headers the headers + * @return the builder + */ + public ScheduledMessageBuilder headers(final Headers headers) { + this.headers = headers; + return this; + } + + /** + * Set the data from a string converting using the + * charset StandardCharsets.UTF_8 + * @param data the data string + * @return the builder + */ + public ScheduledMessageBuilder data(final String data) { + if (data != null) { + this.data = data.getBytes(StandardCharsets.UTF_8); + } + return this; + } + + /** + * Set the data from a string + * @param data the data string + * @param charset the charset, for example {@code StandardCharsets.UTF_8} + * @return the builder + */ + public ScheduledMessageBuilder data(final String data, final Charset charset) { + this.data = data.getBytes(charset); + return this; + } + + /** + * Set the data from a byte array. null data changed to empty byte array + * + * @param data the data + * @return the builder + */ + public ScheduledMessageBuilder data(final byte[] data) { + this.data = data; + return this; + } + + /** + * Schedule with one of the predefined enum values + * @param predefined One of the predefined enum values + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder schedule(Predefined predefined) { + scheduleString = predefined == null ? null : predefined.value; + return this; + } + + /** + * Schedule with one of the predefined enum values + * @param zdt the time to schedule + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleAt(ZonedDateTime zdt) { + scheduleString = zdt == null ? null : "@at " + DateTimeUtils.toRfc3339(zdt); + return this; + } + + /** + * Schedule an interval + * @param every A time specification that complies with Golang's time.ParseDuration() format. + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleEvery(String every) { + every = Validator.emptyAsNull(every); + if (every == null) { + scheduleString = null; + } + else { + scheduleString = "@every " + every; + } + return this; + } + + /** + * Schedule based on standard cron + * @param cron A valid cron string + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleCron(String cron) { + scheduleString = Validator.emptyAsNull(cron); + return this; + } + + public ScheduledMessageBuilder messageTtl(MessageTtl messageTtl) { + this.messageTtl = messageTtl; + return this; + } + + public Message build() { + Validator.required(publishSubject, "Publish Subject is required."); + Validator.required(targetSubject, "Target Subject is required."); + if (Validator.notPrintableOrHasWildGt(publishSubject)) { + Validator.required(publishSubject, "Publish Subject cannot contain '*' or '>'."); + } + if (Validator.notPrintableOrHasWildGt(targetSubject)) { + Validator.required(targetSubject, "Target Subject cannot contain '*' or '>'."); + } + Validator.required(scheduleString, "Schedule is required."); + + if (headers == null) { + headers = new Headers(); + } + headers.put("Nats-Schedule-Target", targetSubject); + headers.put("Nats-Schedule", scheduleString); + if (messageTtl != null) { + headers.put("Nats-Schedule-TTL", messageTtl.getTtlString()); + } + + return NatsMessage.builder() + .subject(publishSubject) + .headers(headers) + .data(data) + .build(); + } +} diff --git a/schedule-message/src/main/javadoc/images/favicon.ico b/schedule-message/src/main/javadoc/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9464855b4b53e64a0b464a138e21734ece9a878b GIT binary patch literal 1150 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 + + + + +JetStream Scheduled Message +

Synadia Logo

+ + + + diff --git a/schedule-message/src/main/resources/placeholder.txt b/schedule-message/src/main/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/schedule-message/src/main/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/schedule-message/src/test/resources/placeholder.txt b/schedule-message/src/test/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/schedule-message/src/test/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/schedule-message/test.bat b/schedule-message/test.bat new file mode 100644 index 0000000..22c2cd1 --- /dev/null +++ b/schedule-message/test.bat @@ -0,0 +1,5 @@ +call gradlew clean build jacocoTestReport +taskkill /F /IM nats-server.exe +start chrome file:///C:/nats/orbit.java/counter/build/reports/jacoco/test/html/index.html +start chrome file:///C:/nats/orbit.java/counter/build/reports/tests/test/index.html + From e94b5fda67cab0bdec03d01b67b8c3408905873b Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 15 Sep 2025 15:55:22 -0400 Subject: [PATCH 030/135] Schedule Message --- .github/workflows/sm-main.yml | 2 +- .github/workflows/sm-pr.yml | 2 +- .github/workflows/sm-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sm-main.yml b/.github/workflows/sm-main.yml index 713ddbc..41d2ca1 100644 --- a/.github/workflows/sm-main.yml +++ b/.github/workflows/sm-main.yml @@ -1,4 +1,4 @@ -name: Chaos Runner Main Snapshot +name: Scheduled Message Main Snapshot on: push: diff --git a/.github/workflows/sm-pr.yml b/.github/workflows/sm-pr.yml index 60e8da3..6d60b9e 100644 --- a/.github/workflows/sm-pr.yml +++ b/.github/workflows/sm-pr.yml @@ -1,4 +1,4 @@ -name: Chaos Runner Pull Request +name: Scheduled Message Pull Request on: pull_request: diff --git a/.github/workflows/sm-release.yml b/.github/workflows/sm-release.yml index 94ccac2..e3abcd9 100644 --- a/.github/workflows/sm-release.yml +++ b/.github/workflows/sm-release.yml @@ -1,4 +1,4 @@ -name: Chaos Runner Release +name: Scheduled Message Release on: push: From ea93221369bbee366d12738aa0f925b1de0876f3 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 15 Sep 2025 16:22:52 -0400 Subject: [PATCH 031/135] Schedule Message --- .../examples/ScheduleAtSpecificTime.java | 14 ++++--- .../examples/ScheduleExampleUtils.java | 41 ++++++++++++++++--- .../synadia/sm/ScheduledMessageBuilder.java | 11 ++++- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java index 6d81a4e..8c39c29 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java @@ -5,11 +5,12 @@ import io.nats.client.*; import io.nats.client.support.DateTimeUtils; -import io.nats.client.support.Debug; import io.synadia.sm.ScheduledMessageBuilder; import java.util.concurrent.CountDownLatch; +import static io.synadia.examples.ScheduleExampleUtils.report; + public class ScheduleAtSpecificTime { public static final String STREAM = "scheduler-stream"; public static final String SCHEDULER_SUBJECT = "scheduler-subject"; @@ -31,24 +32,25 @@ public static void main(String[] args) { // subscribe to the subject that receives the schedule message js.subscribe(SCHEDULER_SUBJECT, d, m -> { - Debug.info("SCHEDULE", m); + report("SCHEDULE", m); m.ack(); }, false); // subscribe to the target subject js.subscribe(TARGET_SUBJECT, d, m -> { - Debug.info("TARGET", m); - m.ack(); latch.countDown(); + report("TARGET", m); + m.ack(); }, false); Message m = new ScheduledMessageBuilder() .publishSubject(SCHEDULER_SUBJECT) .targetSubject(TARGET_SUBJECT) - .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(3)) + .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(10)) + .scheduleCustom("@at 1970-01-01T00:00:00Z") .data("payload") .build(); - Debug.info("PUBLISH", m); + report("PUBLISH", m); js.publish(m); latch.await(); diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java index 6109a69..37fb400 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java @@ -6,10 +6,11 @@ import io.nats.client.Connection; import io.nats.client.JetStreamApiException; import io.nats.client.JetStreamManagement; +import io.nats.client.Message; import io.nats.client.api.StorageType; import io.nats.client.api.StreamConfiguration; import io.nats.client.api.StreamInfo; -import io.nats.client.support.Debug; +import io.nats.client.impl.Headers; import java.io.IOException; @@ -34,15 +35,45 @@ public static void createOrReplaceStream(JetStreamManagement jsm, String stream, .allowMessageSchedules() .build(); StreamInfo si = jsm.addStream(sc); - Debug.info("Created stream: " + si.getConfiguration()); + report("Created stream", si.getConfiguration()); } catch (Exception e) { - Debug.info("Failed creating stream: '' " + e); + report("Failed creating stream", e); System.exit(-1); } } - public static void report(String... strings) { - System.out.println("[" + System.currentTimeMillis() + "] " + String.join(" ", strings)); + public static void report(Object... objects) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Object o : objects) { + if (first) { + first = false; + } + else { + sb.append(" | "); + } + if (o instanceof Message) { + sb.append(toString((Message)o)); + } + else { + sb.append(o.toString()); + } + } + System.out.println("[" + System.currentTimeMillis() + "] " + sb); + } + + public static String toString(Message msg) { + StringBuilder sb = new StringBuilder(System.lineSeparator()) + .append(" Subject: ").append(msg.getSubject()); + Headers h = msg.getHeaders(); + if (h != null && !h.isEmpty()) { + sb.append(System.lineSeparator()).append(" Headers:"); + for (String key : h.keySet()) { + sb.append(System.lineSeparator()).append(" "); + sb.append(key).append("=").append(h.get(key)); + } + } + return sb.toString(); } } diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index a1332cf..8e2eed5 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -151,7 +151,16 @@ public ScheduledMessageBuilder schedule(Predefined predefined) { } /** - * Schedule with one of the predefined enum values + * Schedule with a custom string. Use at your own risk + * @param custom the time to schedule + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleCustom(String custom) { + scheduleString = custom; + return this; + } + /** + * Schedule for at a specific time * @param zdt the time to schedule * @return a ScheduledMessageBuilder object */ From 1ef127df20077ace1129715cee244eb980f31942 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 15 Sep 2025 16:23:26 -0400 Subject: [PATCH 032/135] Schedule Message --- .../java/io/synadia/examples/ScheduleAtSpecificTime.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java index 8c39c29..5526ae6 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java @@ -4,7 +4,6 @@ package io.synadia.examples; import io.nats.client.*; -import io.nats.client.support.DateTimeUtils; import io.synadia.sm.ScheduledMessageBuilder; import java.util.concurrent.CountDownLatch; @@ -38,15 +37,15 @@ public static void main(String[] args) { // subscribe to the target subject js.subscribe(TARGET_SUBJECT, d, m -> { - latch.countDown(); report("TARGET", m); m.ack(); + latch.countDown(); }, false); Message m = new ScheduledMessageBuilder() .publishSubject(SCHEDULER_SUBJECT) .targetSubject(TARGET_SUBJECT) - .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(10)) +// .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(10)) .scheduleCustom("@at 1970-01-01T00:00:00Z") .data("payload") .build(); From 4aa8e73894dfb2574d511c0286e7d2f7495e4f71 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 16 Sep 2025 10:41:22 -0400 Subject: [PATCH 033/135] Schedule Message Examples --- .../examples/ScheduleAtSpecificTime.java | 35 +++++++++++++------ .../examples/ScheduleExampleUtils.java | 6 ++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java index 5526ae6..85b0298 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java @@ -4,6 +4,7 @@ package io.synadia.examples; import io.nats.client.*; +import io.nats.client.support.DateTimeUtils; import io.synadia.sm.ScheduledMessageBuilder; import java.util.concurrent.CountDownLatch; @@ -11,9 +12,15 @@ import static io.synadia.examples.ScheduleExampleUtils.report; public class ScheduleAtSpecificTime { - public static final String STREAM = "scheduler-stream"; - public static final String SCHEDULER_SUBJECT = "scheduler-subject"; - public static final String TARGET_SUBJECT = "target-subject"; + public static final String STREAM = "scheduler"; + + public static final String SCHEDULE_PREFIX = "schedule."; + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; public static void main(String[] args) { try { @@ -23,31 +30,39 @@ public static void main(String[] args) { .build(); try (Connection connection = Nats.connectReconnectOnConnect(options)) { - ScheduleExampleUtils.createOrReplaceStream(connection, STREAM, SCHEDULER_SUBJECT, TARGET_SUBJECT); + ScheduleExampleUtils.createOrReplaceStream(connection, STREAM, STREAM_SUBJECTS); JetStream js = connection.jetStream(); CountDownLatch latch = new CountDownLatch(2); Dispatcher d = connection.createDispatcher(); // subscribe to the subject that receives the schedule message - js.subscribe(SCHEDULER_SUBJECT, d, m -> { + js.subscribe(SCHEDULES, d, m -> { report("SCHEDULE", m); m.ack(); }, false); // subscribe to the target subject - js.subscribe(TARGET_SUBJECT, d, m -> { + js.subscribe(TARGETS, d, m -> { report("TARGET", m); m.ack(); latch.countDown(); }, false); Message m = new ScheduledMessageBuilder() - .publishSubject(SCHEDULER_SUBJECT) - .targetSubject(TARGET_SUBJECT) -// .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(10)) + .publishSubject(SCHEDULE_PREFIX + "custom") + .targetSubject(TARGET_PREFIX + "custom") .scheduleCustom("@at 1970-01-01T00:00:00Z") - .data("payload") + .data("Schedule-In-Past") + .build(); + report("PUBLISH", m); + js.publish(m); + + m = new ScheduledMessageBuilder() + .publishSubject(SCHEDULE_PREFIX + "at") + .targetSubject(TARGET_PREFIX + "at") + .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(5)) + .data("Scheduled-In-Future") .build(); report("PUBLISH", m); js.publish(m); diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java index 37fb400..4b87bd0 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java @@ -66,6 +66,12 @@ public static void report(Object... objects) { public static String toString(Message msg) { StringBuilder sb = new StringBuilder(System.lineSeparator()) .append(" Subject: ").append(msg.getSubject()); + if (msg.getData() == null || msg.getData().length == 0) { + sb.append(" | No Data"); + } + else { + sb.append(" | Data: ").append(new String(msg.getData())); + } Headers h = msg.getHeaders(); if (h != null && !h.isEmpty()) { sb.append(System.lineSeparator()).append(" Headers:"); From 41dce72bf8c8125027c5471edbc8d899f9fe450d Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 16 Sep 2025 12:05:11 -0400 Subject: [PATCH 034/135] Schedule Message Examples --- .../main/java/io/synadia/sm/ScheduledMessageBuilder.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index 8e2eed5..7e9a631 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -5,6 +5,7 @@ import io.nats.client.impl.Headers; import io.nats.client.impl.NatsMessage; import io.nats.client.support.DateTimeUtils; +import io.nats.client.support.NatsJetStreamConstants; import io.nats.client.support.Validator; import java.nio.charset.Charset; @@ -214,10 +215,10 @@ public Message build() { if (headers == null) { headers = new Headers(); } - headers.put("Nats-Schedule-Target", targetSubject); - headers.put("Nats-Schedule", scheduleString); + headers.put(NatsJetStreamConstants.NATS_SCHEDULE_TARGET_HDR, targetSubject); + headers.put(NatsJetStreamConstants.NATS_SCHEDULE_HDR, scheduleString); if (messageTtl != null) { - headers.put("Nats-Schedule-TTL", messageTtl.getTtlString()); + headers.put(NatsJetStreamConstants.NATS_SCHEDULE_TTL_HDR, messageTtl.getTtlString()); } return NatsMessage.builder() From 137e8db5edb5876602378a964884f31fce47cd6f Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 16 Sep 2025 12:23:22 -0400 Subject: [PATCH 035/135] Schedule Message Examples --- ...tSpecificTime.java => ScheduleBasics.java} | 24 +++++++++---- .../examples/ScheduleExampleUtils.java | 35 ------------------- .../synadia/sm/ScheduledMessageBuilder.java | 13 +++---- .../io/synadia/sm/ScheduledStreamUtil.java | 34 ++++++++++++++++++ 4 files changed, 58 insertions(+), 48 deletions(-) rename schedule-message/src/examples/java/io/synadia/examples/{ScheduleAtSpecificTime.java => ScheduleBasics.java} (72%) create mode 100644 schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java similarity index 72% rename from schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java rename to schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java index 85b0298..1a960c5 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleAtSpecificTime.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java @@ -4,14 +4,17 @@ package io.synadia.examples; import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamInfo; import io.nats.client.support.DateTimeUtils; import io.synadia.sm.ScheduledMessageBuilder; +import io.synadia.sm.ScheduledStreamUtil; import java.util.concurrent.CountDownLatch; import static io.synadia.examples.ScheduleExampleUtils.report; -public class ScheduleAtSpecificTime { +public class ScheduleBasics { public static final String STREAM = "scheduler"; public static final String SCHEDULE_PREFIX = "schedule."; @@ -30,9 +33,16 @@ public static void main(String[] args) { .build(); try (Connection connection = Nats.connectReconnectOnConnect(options)) { - ScheduleExampleUtils.createOrReplaceStream(connection, STREAM, STREAM_SUBJECTS); + JetStreamManagement jsm = connection.jetStreamManagement();; JetStream js = connection.jetStream(); + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + // Use the utility to properly create a schedulable stream + StreamInfo si = ScheduledStreamUtil.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + report("Created stream", si.getConfiguration()); + CountDownLatch latch = new CountDownLatch(2); Dispatcher d = connection.createDispatcher(); @@ -50,10 +60,10 @@ public static void main(String[] args) { }, false); Message m = new ScheduledMessageBuilder() - .publishSubject(SCHEDULE_PREFIX + "custom") - .targetSubject(TARGET_PREFIX + "custom") - .scheduleCustom("@at 1970-01-01T00:00:00Z") - .data("Schedule-In-Past") + .publishSubject(SCHEDULE_PREFIX + "now") + .targetSubject(TARGET_PREFIX + "now") + .scheduleImmediate() + .data("Schedule-Now") .build(); report("PUBLISH", m); js.publish(m); @@ -62,7 +72,7 @@ public static void main(String[] args) { .publishSubject(SCHEDULE_PREFIX + "at") .targetSubject(TARGET_PREFIX + "at") .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(5)) - .data("Scheduled-In-Future") + .data("Scheduled-At") .build(); report("PUBLISH", m); js.publish(m); diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java index 4b87bd0..9e76501 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java @@ -3,46 +3,11 @@ package io.synadia.examples; -import io.nats.client.Connection; -import io.nats.client.JetStreamApiException; -import io.nats.client.JetStreamManagement; import io.nats.client.Message; -import io.nats.client.api.StorageType; -import io.nats.client.api.StreamConfiguration; -import io.nats.client.api.StreamInfo; import io.nats.client.impl.Headers; -import java.io.IOException; - public class ScheduleExampleUtils { - public static void createOrReplaceStream(Connection connection, String stream, String... subjects) throws IOException, JetStreamApiException { - createOrReplaceStream(connection.jetStreamManagement(), stream, subjects); - } - - public static void createOrReplaceStream(JetStreamManagement jsm, String stream, String... subjects) throws IOException, JetStreamApiException { - report("createOrReplaceStream"); - try { - jsm.deleteStream(stream); - } - catch (Exception ignore) {} - - try { - StreamConfiguration sc = StreamConfiguration.builder() - .name(stream) - .storageType(StorageType.Memory) - .subjects(subjects) - .allowMessageSchedules() - .build(); - StreamInfo si = jsm.addStream(sc); - report("Created stream", si.getConfiguration()); - } - catch (Exception e) { - report("Failed creating stream", e); - System.exit(-1); - } - } - public static void report(Object... objects) { StringBuilder sb = new StringBuilder(); boolean first = true; diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index 7e9a631..0b81062 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -13,7 +13,7 @@ import java.time.ZonedDateTime; /** - * Class to make setting a per message ttl easier. + * Class to make a message that can be published to a stream that allows message scheduling */ public class ScheduledMessageBuilder { @@ -152,14 +152,15 @@ public ScheduledMessageBuilder schedule(Predefined predefined) { } /** - * Schedule with a custom string. Use at your own risk - * @param custom the time to schedule + * Schedule with a time of now minus 1 second, + * which will be in the past by the time it gets to the server, + * so the scheduled message will be published immediately. * @return a ScheduledMessageBuilder object */ - public ScheduledMessageBuilder scheduleCustom(String custom) { - scheduleString = custom; - return this; + public ScheduledMessageBuilder scheduleImmediate() { + return scheduleAt(DateTimeUtils.gmtNow().minusSeconds(1)); } + /** * Schedule for at a specific time * @param zdt the time to schedule diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java new file mode 100644 index 0000000..26147c9 --- /dev/null +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java @@ -0,0 +1,34 @@ +package io.synadia.sm; + +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamConfiguration; +import io.nats.client.api.StreamInfo; + +import java.io.IOException; + +/** + * Class to make setting a per message ttl easier. + */ +public abstract class ScheduledStreamUtil { + + public static StreamInfo createSchedulableStream(JetStreamManagement jsm, String streamName, StorageType storageType, String... subjects) throws JetStreamApiException, IOException { + StreamConfiguration sc = StreamConfiguration.builder() + .name(streamName) + .storageType(storageType) + .subjects(subjects) + .allowMessageSchedules() + .allowMessageTtl() + .build(); + return jsm.addStream(sc); + } + + public static StreamInfo createSchedulableStream(JetStreamManagement jsm, StreamConfiguration startingStreamConfig) throws JetStreamApiException, IOException { + StreamConfiguration sc = StreamConfiguration.builder(startingStreamConfig) + .allowMessageSchedules() + .allowMessageTtl() + .build(); + return jsm.addStream(sc); + } +} From e33ac3cb742d146c834c9170b26562d9b8b07ed6 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 16 Sep 2025 12:49:42 -0400 Subject: [PATCH 036/135] Schedule Message Readme --- schedule-message/README.md | 92 ++++++++++++- .../io/synadia/examples/ScheduleBasics.java | 4 +- .../io/synadia/sm/PredefinedSchedules.java | 47 +++++++ .../synadia/sm/ScheduledMessageBuilder.java | 124 ++++++------------ 4 files changed, 181 insertions(+), 86 deletions(-) create mode 100644 schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java diff --git a/schedule-message/README.md b/schedule-message/README.md index 2f943c2..b132ef3 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -2,9 +2,6 @@ # JetStream Scheduled Message -https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md - - **Current Release**: N/A   **Current Snapshot**: 0.1.0-SNAPSHOT   **Gradle and Maven** `io.synadia:scheduled-message` @@ -15,6 +12,95 @@ https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) [![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) + +### Building a Scheduled Message + +A scheduled message is just a normal message with some extra headers. + +It consists of a subject that holds the schedule message, +a subject that is the target subject for the schedule, +the scheduling information which can be a specific time or a standard cron based schedule. + +The `ScheduledMessageBuilder` makes it easy to create this using a builder pattern. + +### Basic message content + +You can add message data and custom headers like a normal message with these builder methods: + +``` +scheduleSubject(String scheduleSubject) // set the primary subject +targetSubject(String targetSubject) // set the subject that is the target of the schedule +data(byte[] data) // set the data from a byte array +data(String data) // set the data from a UTF-8 string +data(String data, Charset charset) // set the data from a string +headers(Headers headers) // set user headers +copy(Message message) // copy the subject, data and headers from an existing message +``` + +### Scheduling variations + +There are several scheduling variations. Only the last one given to the builder is used. + +``` +scheduleAt(ZonedDateTime zdt) +scheduleImmediate() +schedule(Predefined predefined) +scheduleEvery(String every) +scheduleCron(String cron) +``` + +### TTL + +You can set a scheduled message to have a TTL + +``` +messageTtl(MessageTtl messageTtl) +``` + +### Predefined Schedules + +There is an enum that pre-defines some repeating schedule behavior. +``` +/** + * Run once a year, midnight, Jan. 1st. Same as Yearly. Equivalent to cron string 0 0 0 1 1 * + */ +Annually("@annually"), + +/** + * Run once a year, midnight, Jan. 1st. Same as Annually. Equivalent to cron string 0 0 0 1 1 * + */ +Yearly("@yearly"), + +/** + * Run once a month, midnight, first of month. Same as cron format 0 0 0 1 * * + */ +Monthly("@monthly"), + +/** + * Run once a week, midnight between Sat/Sun. Equivalent to cron string 0 0 0 * * 0 + */ +Weekly("@weekly"), + +/** + * Run once a day, midnight. Same as Daily. Equivalent to cron string 0 0 0 * * * + */ +Midnight("@midnight"), + +/** + * Run once a day, midnight. Same as Midnight. Equivalent to cron string 0 0 0 * * * + */ +Daily("@daily"), + +/** + * Run once an hour, beginning of hour. Equivalent to cron string 0 0 * * * * + */ +Hourly("@hourly"); +``` + +### ADR + +The original feature design document: [JetStream Message Scheduler ADR-51](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md) + --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java index 1a960c5..5138df3 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java @@ -60,7 +60,7 @@ public static void main(String[] args) { }, false); Message m = new ScheduledMessageBuilder() - .publishSubject(SCHEDULE_PREFIX + "now") + .scheduleSubject(SCHEDULE_PREFIX + "now") .targetSubject(TARGET_PREFIX + "now") .scheduleImmediate() .data("Schedule-Now") @@ -69,7 +69,7 @@ public static void main(String[] args) { js.publish(m); m = new ScheduledMessageBuilder() - .publishSubject(SCHEDULE_PREFIX + "at") + .scheduleSubject(SCHEDULE_PREFIX + "at") .targetSubject(TARGET_PREFIX + "at") .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(5)) .data("Scheduled-At") diff --git a/schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java b/schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java new file mode 100644 index 0000000..83f77fc --- /dev/null +++ b/schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java @@ -0,0 +1,47 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.sm; + +public enum PredefinedSchedules { + /** + * Run once a year, midnight, Jan. 1st. Same as Yearly. Equivalent to cron string 0 0 0 1 1 * + */ + Annually("@annually"), + + /** + * Run once a year, midnight, Jan. 1st. Same as Annually. Equivalent to cron string 0 0 0 1 1 * + */ + Yearly("@yearly"), + + /** + * Run once a month, midnight, first of month. Same as cron format 0 0 0 1 * * + */ + Monthly("@monthly"), + + /** + * Run once a week, midnight between Sat/Sun. Equivalent to cron string 0 0 0 * * 0 + */ + Weekly("@weekly"), + + /** + * Run once a day, midnight. Same as Daily. Equivalent to cron string 0 0 0 * * * + */ + Midnight("@midnight"), + + /** + * Run once a day, midnight. Same as Midnight. Equivalent to cron string 0 0 0 * * * + */ + Daily("@daily"), + + /** + * Run once an hour, beginning of hour. Equivalent to cron string 0 0 * * * * + */ + Hourly("@hourly"); + + final String value; + + PredefinedSchedules(String value) { + this.value = value; + } +} diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index 0b81062..c16804f 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -1,3 +1,6 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + package io.synadia.sm; import io.nats.client.Message; @@ -17,72 +20,22 @@ */ public class ScheduledMessageBuilder { - public enum Predefined { - /** - * Run once a year, midnight, Jan. 1st. Same as Yearly. Equivalent to cron string 0 0 0 1 1 * - */ - Annually("@annually"), - - /** - * Run once a year, midnight, Jan. 1st. Same as Annually. Equivalent to cron string 0 0 0 1 1 * - */ - Yearly("@yearly"), - - /** - * Run once a month, midnight, first of month. Same as cron format 0 0 0 1 * * - */ - Monthly("@monthly"), - - /** - * Run once a week, midnight between Sat/Sun. Equivalent to cron string 0 0 0 * * 0 - */ - Weekly("@weekly"), - - /** - * Run once a day, midnight. Same as Daily. Equivalent to cron string 0 0 0 * * * - */ - Midnight("@midnight"), - - /** - * Run once a day, midnight. Same as Midnight. Equivalent to cron string 0 0 0 * * * - */ - Daily("@daily"), - - /** - * Run once an hour, beginning of hour. Equivalent to cron string 0 0 * * * * - */ - Hourly("@hourly"); - - final String value; - - Predefined(String value) { - this.value = value; - } - } - private String scheduleString; - private String publishSubject; + private String scheduleSubject; private String targetSubject; private Headers headers; private byte[] data; private MessageTtl messageTtl; - public ScheduledMessageBuilder() { - } - - public ScheduledMessageBuilder(Message message) { - this.publishSubject = message.getSubject(); - this.headers = message.getHeaders(); - this.data = message.getData(); - } + public ScheduledMessageBuilder() {} /** - * Set the publish subject - * @param publishSubject the publish subject + * Set the schedule subject + * @param scheduleSubject the schedule subject * @return the builder */ - public ScheduledMessageBuilder publishSubject(final String publishSubject) { - this.publishSubject = publishSubject; + public ScheduledMessageBuilder scheduleSubject(String scheduleSubject) { + this.scheduleSubject = scheduleSubject; return this; } @@ -91,18 +44,18 @@ public ScheduledMessageBuilder publishSubject(final String publishSubject) { * @param targetSubject the target subject * @return the builder */ - public ScheduledMessageBuilder targetSubject(final String targetSubject) { + public ScheduledMessageBuilder targetSubject(String targetSubject) { this.targetSubject = targetSubject; return this; } /** - * Set the headers - * @param headers the headers + * Set the data from a byte array. null data changed to empty byte array + * @param data the data * @return the builder */ - public ScheduledMessageBuilder headers(final Headers headers) { - this.headers = headers; + public ScheduledMessageBuilder data(byte[] data) { + this.data = data; return this; } @@ -112,7 +65,7 @@ public ScheduledMessageBuilder headers(final Headers headers) { * @param data the data string * @return the builder */ - public ScheduledMessageBuilder data(final String data) { + public ScheduledMessageBuilder data(String data) { if (data != null) { this.data = data.getBytes(StandardCharsets.UTF_8); } @@ -125,34 +78,43 @@ public ScheduledMessageBuilder data(final String data) { * @param charset the charset, for example {@code StandardCharsets.UTF_8} * @return the builder */ - public ScheduledMessageBuilder data(final String data, final Charset charset) { + public ScheduledMessageBuilder data(String data, final Charset charset) { this.data = data.getBytes(charset); return this; } /** - * Set the data from a byte array. null data changed to empty byte array - * - * @param data the data + * Set the headers + * @param headers the headers * @return the builder */ - public ScheduledMessageBuilder data(final byte[] data) { - this.data = data; + public ScheduledMessageBuilder headers(Headers headers) { + this.headers = headers; return this; } /** - * Schedule with one of the predefined enum values - * @param predefined One of the predefined enum values + * Copy the subject, data and headers from an existing message + * @param message the message + */ + public ScheduledMessageBuilder copy(Message message) { + scheduleSubject(message.getSubject()); + headers(message.getHeaders()); + return data(message.getData()); + } + + /** + * Schedule for at a specific time + * @param zdt the time to schedule * @return a ScheduledMessageBuilder object */ - public ScheduledMessageBuilder schedule(Predefined predefined) { - scheduleString = predefined == null ? null : predefined.value; + public ScheduledMessageBuilder scheduleAt(ZonedDateTime zdt) { + scheduleString = zdt == null ? null : "@at " + DateTimeUtils.toRfc3339(zdt); return this; } /** - * Schedule with a time of now minus 1 second, + * Schedule to run immediately. This is like scheduleAt with time of "now" minus 1 second, * which will be in the past by the time it gets to the server, * so the scheduled message will be published immediately. * @return a ScheduledMessageBuilder object @@ -162,12 +124,12 @@ public ScheduledMessageBuilder scheduleImmediate() { } /** - * Schedule for at a specific time - * @param zdt the time to schedule + * Schedule with one of the predefined enum values + * @param predefined One of the predefined enum values * @return a ScheduledMessageBuilder object */ - public ScheduledMessageBuilder scheduleAt(ZonedDateTime zdt) { - scheduleString = zdt == null ? null : "@at " + DateTimeUtils.toRfc3339(zdt); + public ScheduledMessageBuilder schedule(PredefinedSchedules predefined) { + scheduleString = predefined == null ? null : predefined.value; return this; } @@ -203,10 +165,10 @@ public ScheduledMessageBuilder messageTtl(MessageTtl messageTtl) { } public Message build() { - Validator.required(publishSubject, "Publish Subject is required."); + Validator.required(scheduleSubject, "Publish Subject is required."); Validator.required(targetSubject, "Target Subject is required."); - if (Validator.notPrintableOrHasWildGt(publishSubject)) { - Validator.required(publishSubject, "Publish Subject cannot contain '*' or '>'."); + if (Validator.notPrintableOrHasWildGt(scheduleSubject)) { + Validator.required(scheduleSubject, "Publish Subject cannot contain '*' or '>'."); } if (Validator.notPrintableOrHasWildGt(targetSubject)) { Validator.required(targetSubject, "Target Subject cannot contain '*' or '>'."); @@ -223,7 +185,7 @@ public Message build() { } return NatsMessage.builder() - .subject(publishSubject) + .subject(scheduleSubject) .headers(headers) .data(data) .build(); From a9e36c3111a26e6dada3f544bbc351425e22e039 Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 17 Sep 2025 16:08:19 -0400 Subject: [PATCH 037/135] No Headers is Orbit only --- .../main/java/io/synadia/counter/Counter.java | 28 +++++++++++++++++-- .../direct/MessageBatchGetRequest.java | 20 ------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/counter/src/main/java/io/synadia/counter/Counter.java b/counter/src/main/java/io/synadia/counter/Counter.java index d0da300..47f2200 100644 --- a/counter/src/main/java/io/synadia/counter/Counter.java +++ b/counter/src/main/java/io/synadia/counter/Counter.java @@ -17,6 +17,9 @@ import java.util.List; import java.util.concurrent.LinkedBlockingQueue; +import static io.nats.client.support.ApiConstants.LAST_BY_SUBJECT; +import static io.nats.client.support.ApiConstants.NO_HDR; +import static io.nats.client.support.JsonUtils.*; import static io.nats.client.support.Validator.required; import static io.synadia.counter.CounterUtils.INCREMENT_HEADER; import static io.synadia.counter.CounterUtils.extractVal; @@ -132,9 +135,30 @@ public BigInteger setViaAdd(String subject, BigInteger value) throws JetStreamAp return _add(subject, value.subtract(bi).toString()); } + static class NoHeadersMessageGetRequest extends MessageGetRequest { + public NoHeadersMessageGetRequest(String lastBySubject) { + super(-1, lastBySubject, null, null); + } + + @Override + public boolean isLastBySubject() { + // this makes sure that the underlying getMessage call + // doesn't use the JSAPI_DIRECT_GET_LAST api + return false; + } + + @Override + public @NonNull String toJson() { + StringBuilder sb = beginJson(); + addField(sb, LAST_BY_SUBJECT, getLastBySubject()); + addField(sb, NO_HDR, true); + return endJson(sb).toString(); + } + } + public BigInteger get(String subject) throws JetStreamApiException, IOException { validateSingleSubject(subject); - MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject).noHeaders()); + MessageInfo mi = jsm.getMessage(streamName, new NoHeadersMessageGetRequest(subject)); return extractVal(mi.getData()); } @@ -161,7 +185,7 @@ public LinkedBlockingQueue getMultiple(String... subjects) public LinkedBlockingQueue getMultiple(List subjects) { LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); - MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects).noHeaders(); + MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterValueResponse(mi))); return queue; } diff --git a/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java b/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java index 501ae3c..7fceaa2 100644 --- a/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java +++ b/direct-batch/src/main/java/io/synadia/direct/MessageBatchGetRequest.java @@ -23,7 +23,6 @@ public class MessageBatchGetRequest implements JsonSerializable { private final List multiLastBySubjects; private final long upToSequence; private final ZonedDateTime upToTime; - private final boolean noHeaders; // batch constructor private MessageBatchGetRequest(String subject, @@ -40,7 +39,6 @@ private MessageBatchGetRequest(String subject, this.upToSequence = -1; this.upToTime = null; this.minSequence = startTime == null && minSequence < 1 ? 1 : minSequence; - noHeaders = false; } // multi last for constructor @@ -56,23 +54,6 @@ private MessageBatchGetRequest(List subjects, long upToSequence, ZonedDa this.multiLastBySubjects = subjects; this.upToSequence = upToSequence; this.upToTime = upToTime; - noHeaders = false; - } - - private MessageBatchGetRequest(MessageBatchGetRequest r, boolean noHeaders) { - this.batch = r.batch; - this.nextBySubject = r.nextBySubject; - this.maxBytes = r.maxBytes; - this.minSequence = r.minSequence; - this.startTime = r.startTime; - this.multiLastBySubjects = r.multiLastBySubjects; - this.upToSequence = r.upToSequence; - this.upToTime = r.upToTime; - this.noHeaders = noHeaders; - } - - public MessageBatchGetRequest noHeaders() { - return new MessageBatchGetRequest(this, true); } /** @@ -283,7 +264,6 @@ public String toJson() { addStrings(sb, MULTI_LAST, multiLastBySubjects); addField(sb, UP_TO_SEQ, upToSequence); addField(sb, UP_TO_TIME, upToTime); - addFldWhenTrue(sb, NO_HDR, noHeaders); return endJson(sb).toString(); } From 8aadfb5ec141af8ac837f199dfdcf78d035338d1 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 18 Sep 2025 11:17:35 -0400 Subject: [PATCH 038/135] Remove no header --- .../examples/CounterContextExample.java | 52 +++---------------- .../main/java/io/synadia/counter/Counter.java | 37 +------------ .../synadia/counter/CounterEntryResponse.java | 9 ++++ .../synadia/counter/CounterValueResponse.java | 36 ------------- 4 files changed, 18 insertions(+), 116 deletions(-) delete mode 100644 counter/src/main/java/io/synadia/counter/CounterValueResponse.java diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index b14bad5..3704083 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -11,7 +11,6 @@ import io.nats.client.api.StreamConfiguration; import io.synadia.counter.Counter; import io.synadia.counter.CounterEntryResponse; -import io.synadia.counter.CounterValueResponse; import java.math.BigInteger; import java.util.concurrent.LinkedBlockingQueue; @@ -98,37 +97,20 @@ public static void main(String[] args) throws Exception { } // ---------------------------------------------------------------------------------------------------- - System.out.println("\n4.1: getMultiple(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterValueResponse objects for multiple subjects. Maybe to total them up?\""); - LinkedBlockingQueue vResponses = counter.getMultiple("cs.A", "cs.B", "cs.C"); - BigInteger total = BigInteger.ZERO; - CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); - while (vr != null && vr.isValue()) { - System.out.println(" " + vr); - total = total.add(vr.getValue()); - vr = vResponses.poll(10, TimeUnit.MILLISECONDS); - } - System.out.println(" " + vr + " -> No more values."); - System.out.println(" Values totaled: " + total); - - System.out.println("\n4.2: getEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterEntryResponse objects for multiple subjects."); + System.out.println("\n4.1: getEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterEntryResponse objects for multiple subjects."); LinkedBlockingQueue eResponses = counter.getEntries("cs.A", "cs.B", "cs.C"); + BigInteger total = BigInteger.ZERO; CounterEntryResponse er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { System.out.println(" " + er); + // the entry response has a method to simplify getting the value + total = total.add(er.getValue()); er = eResponses.poll(10, TimeUnit.MILLISECONDS); } System.out.println(" " + er + " -> No more entries."); + System.out.println(" Values totaled: " + total); - System.out.println("\n4.3: getMultiple(\"cs.*\") - Get the CounterValueResponse objects for wildcard subject(s)."); - vResponses = counter.getMultiple("cs.*"); - vr = vResponses.poll(1, TimeUnit.SECONDS); - while (vr != null && vr.isValue()) { - System.out.println(" " + vr); - vr = vResponses.poll(10, TimeUnit.MILLISECONDS); - } - System.out.println(" " + vr + " -> No more values."); - - System.out.println("\n4.4: getEntries(\"cs.*\") - Get CounterEntryResponse objects for wildcard subject(s)."); + System.out.println("\n4.2: getEntries(\"cs.*\") - Get CounterEntryResponse objects for wildcard subject(s)."); eResponses = counter.getEntries("cs.*"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { @@ -162,16 +144,7 @@ public static void main(String[] args) throws Exception { System.out.println(" get(\"cs.did-not-exist\") -> " + counter.get("cs.did-not-exist")); // ---------------------------------------------------------------------------------------------------- - System.out.println("\n6.1: getMultiple(\"cs.no-counters\", \"cs.also-counters\") - getMultiple but no subjects have counters."); - vResponses = counter.getMultiple("cs.no-counters", "cs.also-counters"); - vr = vResponses.poll(1, TimeUnit.SECONDS); - while (vr != null && vr.isValue()) { - System.out.println(" " + er); - vr = vResponses.poll(10, TimeUnit.MILLISECONDS); - } - System.out.println(" " + vr); - - System.out.println("\n6.2: getEntries(\"cs.no-counters\", \"cs.also-counters\") - getEntries but no subjects have counters."); + System.out.println("\n6.1: getEntries(\"cs.no-counters\", \"cs.also-counters\") - getEntries but no subjects have counters."); eResponses = counter.getEntries("cs.no-counters", "cs.also-counters"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { @@ -181,16 +154,7 @@ public static void main(String[] args) throws Exception { System.out.println(" " + er); // ---------------------------------------------------------------------------------------------------- - System.out.println("\n7.1: getMultiple(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - getMultiple when some subjects have counters."); - vResponses = counter.getMultiple("cs.no-counters", "cs.A", "cs.B", "cs.C"); - vr = vResponses.poll(1, TimeUnit.SECONDS); - while (vr != null && vr.isValue()) { - System.out.println(" " + vr); - vr = vResponses.poll(10, TimeUnit.MILLISECONDS); - } - System.out.println(" " + vr + " -> No more values."); - - System.out.println("\n7.2: getEntries(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - getEntries when some subjects have counters."); + System.out.println("\n7.1: getEntries(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - getEntries when some subjects have counters."); eResponses = counter.getEntries("cs.no-counters", "cs.A", "cs.B", "cs.C"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { diff --git a/counter/src/main/java/io/synadia/counter/Counter.java b/counter/src/main/java/io/synadia/counter/Counter.java index 47f2200..95a9280 100644 --- a/counter/src/main/java/io/synadia/counter/Counter.java +++ b/counter/src/main/java/io/synadia/counter/Counter.java @@ -17,9 +17,6 @@ import java.util.List; import java.util.concurrent.LinkedBlockingQueue; -import static io.nats.client.support.ApiConstants.LAST_BY_SUBJECT; -import static io.nats.client.support.ApiConstants.NO_HDR; -import static io.nats.client.support.JsonUtils.*; import static io.nats.client.support.Validator.required; import static io.synadia.counter.CounterUtils.INCREMENT_HEADER; import static io.synadia.counter.CounterUtils.extractVal; @@ -135,30 +132,9 @@ public BigInteger setViaAdd(String subject, BigInteger value) throws JetStreamAp return _add(subject, value.subtract(bi).toString()); } - static class NoHeadersMessageGetRequest extends MessageGetRequest { - public NoHeadersMessageGetRequest(String lastBySubject) { - super(-1, lastBySubject, null, null); - } - - @Override - public boolean isLastBySubject() { - // this makes sure that the underlying getMessage call - // doesn't use the JSAPI_DIRECT_GET_LAST api - return false; - } - - @Override - public @NonNull String toJson() { - StringBuilder sb = beginJson(); - addField(sb, LAST_BY_SUBJECT, getLastBySubject()); - addField(sb, NO_HDR, true); - return endJson(sb).toString(); - } - } - public BigInteger get(String subject) throws JetStreamApiException, IOException { validateSingleSubject(subject); - MessageInfo mi = jsm.getMessage(streamName, new NoHeadersMessageGetRequest(subject)); + MessageInfo mi = jsm.getMessage(streamName, MessageGetRequest.lastForSubject(subject)); return extractVal(mi.getData()); } @@ -179,17 +155,6 @@ public BigInteger getOrElse(String subject, BigInteger dflt) throws IOException } } - public LinkedBlockingQueue getMultiple(String... subjects) { - return getMultiple(Arrays.asList(subjects)); - } - - public LinkedBlockingQueue getMultiple(List subjects) { - LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); - MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); - dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterValueResponse(mi))); - return queue; - } - public CounterEntry getEntry(String subject) throws JetStreamApiException, IOException { validateSingleSubject(subject); MessageInfo mi = jsm.getLastMessage(streamName, subject); diff --git a/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java b/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java index b76e351..c72a6fd 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java +++ b/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java @@ -6,6 +6,10 @@ import io.nats.client.api.MessageInfo; import org.jspecify.annotations.Nullable; +import java.math.BigInteger; + +import static io.synadia.counter.CounterUtils.extractVal; + public class CounterEntryResponse extends CounterResponse { CounterEntryResponse(MessageInfo mi) { @@ -20,6 +24,11 @@ public boolean isEntry() { return mi.isMessage(); } + @Nullable + public BigInteger getValue() { + return mi.isMessage() ? extractVal(mi.getData()) : null; + } + @Nullable public CounterEntry getEntry() { return mi.isMessage() ? new CounterEntry(mi) : null; diff --git a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java b/counter/src/main/java/io/synadia/counter/CounterValueResponse.java deleted file mode 100644 index 6e40d85..0000000 --- a/counter/src/main/java/io/synadia/counter/CounterValueResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. -// See LICENSE and NOTICE file for details. - -package io.synadia.counter; - -import io.nats.client.api.MessageInfo; -import org.jspecify.annotations.Nullable; - -import java.math.BigInteger; - -import static io.synadia.counter.CounterUtils.extractVal; - -public class CounterValueResponse extends CounterResponse { - - CounterValueResponse(MessageInfo mi) { - super(mi); - } - - /** - * Whether this CounterValue is a regular value as opposed to an error/status - * @return true if the CounterValue is a regular value - */ - public boolean isValue() { - return mi.isMessage(); - } - - @Nullable - public BigInteger getValue() { - return mi.isMessage() ? extractVal(mi.getData()) : null; - } - - @Override - public String toString() { - return "CounterValueResponse{ " + (isValue() ? getValue() : getStatus() ) + " }"; - } -} From 1f86d0ec9d5267e38883ec8ac42140f2497e4d29 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 18 Sep 2025 14:27:54 -0400 Subject: [PATCH 039/135] Initial Batch Publish --- batch-publish/.gitignore | 77 +++ batch-publish/LICENSE | 201 ++++++++ batch-publish/NOTICE | 5 + batch-publish/README.md | 18 + batch-publish/build.gradle | 209 ++++++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + batch-publish/gradlew | 183 +++++++ batch-publish/gradlew.bat | 103 ++++ batch-publish/settings.gradle | 10 + .../synadia/examples/BatchPublishExample.java | 93 ++++ .../io/synadia/bp/BatchPublishException.java | 10 + .../java/io/synadia/bp/BatchPublisher.java | 146 ++++++ .../src/main/javadoc/images/favicon.ico | Bin 0 -> 1150 bytes .../src/main/javadoc/images/large-logo.png | Bin 0 -> 6533 bytes .../src/main/javadoc/images/synadia-logo.png | Bin 0 -> 19014 bytes batch-publish/src/main/javadoc/overview.html | 13 + .../src/main/resources/placeholder.txt | 2 + .../java/io/nats/client/support/Debug.java | 461 ++++++++++++++++++ .../src/test/resources/placeholder.txt | 1 + .../examples/CounterContextExample.java | 3 +- .../java/io/synadia/counter/CounterTests.java | 22 - 22 files changed, 1539 insertions(+), 23 deletions(-) create mode 100644 batch-publish/.gitignore create mode 100644 batch-publish/LICENSE create mode 100644 batch-publish/NOTICE create mode 100644 batch-publish/README.md create mode 100644 batch-publish/build.gradle create mode 100644 batch-publish/gradle/wrapper/gradle-wrapper.jar create mode 100644 batch-publish/gradle/wrapper/gradle-wrapper.properties create mode 100644 batch-publish/gradlew create mode 100644 batch-publish/gradlew.bat create mode 100644 batch-publish/settings.gradle create mode 100644 batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java create mode 100644 batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java create mode 100644 batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java create mode 100644 batch-publish/src/main/javadoc/images/favicon.ico create mode 100644 batch-publish/src/main/javadoc/images/large-logo.png create mode 100644 batch-publish/src/main/javadoc/images/synadia-logo.png create mode 100644 batch-publish/src/main/javadoc/overview.html create mode 100644 batch-publish/src/main/resources/placeholder.txt create mode 100644 batch-publish/src/test/java/io/nats/client/support/Debug.java create mode 100644 batch-publish/src/test/resources/placeholder.txt diff --git a/batch-publish/.gitignore b/batch-publish/.gitignore new file mode 100644 index 0000000..b3e2ca5 --- /dev/null +++ b/batch-publish/.gitignore @@ -0,0 +1,77 @@ + +# NATS stuff # +############## +gnatsd.log +*.csv + +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +/bin + +# Packages # +############ +*.7z +*.dmg +*.gz +*.iso +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log + +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db + +# Editor Files # +################ +*~ +*.swp +.sts4-cache/* + +# Gradle Files # +################ +.gradle +.m2 + +# Build output directies +/target +*/target +/build +*/build + +# IntelliJ specific files/directories +out +.idea +*.ipr +*.iws +*.iml +atlassian-ide-plugin.xml + +# Eclipse specific files/directories +.classpath +.project +.settings +.metadata + +# NetBeans specific files/directories +.nbattrs + +# VSCode +.vscode/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/target/ diff --git a/batch-publish/LICENSE b/batch-publish/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/batch-publish/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/batch-publish/NOTICE b/batch-publish/NOTICE new file mode 100644 index 0000000..ff3c8b4 --- /dev/null +++ b/batch-publish/NOTICE @@ -0,0 +1,5 @@ +Orbit Java +Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. + +This product includes software developed at +Synadia Communications Inc. \ No newline at end of file diff --git a/batch-publish/README.md b/batch-publish/README.md new file mode 100644 index 0000000..67ec123 --- /dev/null +++ b/batch-publish/README.md @@ -0,0 +1,18 @@ +![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) + +# Batch Publish + +**Current Release**: 0.0.0 + **Current Snapshot**: 0.0.1-SNAPSHOT +  **Gradle and Maven** `io.synadia:batch-publish` +[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) + +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-00BC8E?labelColor=grey&style=flat) +[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish) +[![javadoc](https://javadoc.io/badge2/io.synadia/batch-publish/javadoc.svg)](https://javadoc.io/doc/io.synadia/batch-publish) + + +--- +Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle new file mode 100644 index 0000000..171e04d --- /dev/null +++ b/batch-publish/build.gradle @@ -0,0 +1,209 @@ +import aQute.bnd.gradle.Bundle +import org.gradle.internal.os.OperatingSystem + +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'jacoco' + id 'com.github.kt3k.coveralls' version '2.12.0' + id 'biz.aQute.bnd.builder' version '5.1.2' + id "org.gradle.test-retry" version "1.1.9" + id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' + id 'signing' +} + +def jarVersion = "0.1.3" +group = 'io.synadia' + +def isMerge = System.getenv("BUILD_EVENT") == "push" +def isRelease = System.getenv("BUILD_EVENT") == "release" + +// version is the variable the build actually uses. +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() + maven { + url "https://repo1.maven.org/maven2/" + } + maven { + url "https://central.sonatype.com/repository/maven-snapshots/" + } +} + +dependencies { + implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'org.jspecify:jspecify:1.0.0' + + testImplementation 'io.nats:jnats-server-runner:1.2.8' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' +} + +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' +} + +sourceSets { + main { + java { + srcDirs = ['src/main/java','src/examples/java'] + } + } + test { + java { + srcDirs = ['src/test/java'] + } + } +} + +tasks.register('bundle', Bundle) { + from sourceSets.main.output + exclude("io/synadia/examples/**") +} + +jar { + manifest { + attributes('Automatic-Module-Name': 'io.synadia.batch-publish') + } + bnd (['Implementation-Title': 'Batch Publish', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io'] + ) + exclude("io/synadia/examples/**") +} + +test { + // Use junit platform for unit tests + useJUnitPlatform() +} + +javadoc { + options.overview = 'src/main/javadoc/overview.html' // relative to source root + source = sourceSets.main.allJava + title = "Synadia Communications Inc. Batch Publish" + classpath = sourceSets.main.runtimeClasspath + doLast { + if (!OperatingSystem.current().isWindows()) { + exec { + println "Updating favicon on all html files" + workingDir 'build/docs/javadoc' + // Only on linux, mac at this point + commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' + } + copy { + println "Copying images to javadoc folder" + from 'src/main/javadoc/images' + into 'build/docs/javadoc' + } + } + } +} + +tasks.register('examplesJar', Jar) { + archiveClassifier.set('examples') + manifest { + attributes('Implementation-Title': 'Batch Publish Examples', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io') + } + from(sourceSets.main.output) { + include "io/synadia/examples/**" + } +} + +tasks.register('javadocJar', Jar) { + archiveClassifier.set('javadoc') + from javadoc +} + +tasks.register('sourcesJar', Jar) { + archiveClassifier.set('sources') + from sourceSets.main.allSource +} + +jacoco { + toolVersion = "0.8.6" +} + +jacocoTestReport { + reports { + xml.enabled = true // coveralls plugin depends on xml format report + html.enabled = true + } + afterEvaluate { // only report on main library not examples + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, + exclude: ['**/examples**']) + })) + } +} + +artifacts { + archives javadocJar, sourcesJar, examplesJar +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username = System.getenv('OSSRH_USERNAME') + password = System.getenv('OSSRH_PASSWORD') + } + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact examplesJar + artifact javadocJar + pom { + name = 'batch-publish' + packaging = 'jar' + groupId = group + artifactId = archivesBaseName + description = 'Synadia Communications Inc. Batch Publish' + url = 'https://github.com/synadia-io/orbit.java' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = "synadia" + name = "Synadia" + email = "info@synadia.com" + url = "https://synadia.io" + } + } + scm { + url = 'https://github.com/synadia-io/orbit.java' + } + } + } + } +} + +if (isRelease) { + signing { + def signingKeyId = System.getenv('SIGNING_KEY_ID') + def signingKey = System.getenv('SIGNING_KEY') + def signingPassword = System.getenv('SIGNING_PASSWORD') + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + sign configurations.archives + sign publishing.publications.mavenJava + } +} diff --git a/batch-publish/gradle/wrapper/gradle-wrapper.jar b/batch-publish/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 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 literal 0 HcmV?d00001 diff --git a/batch-publish/gradle/wrapper/gradle-wrapper.properties b/batch-publish/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..442d913 --- /dev/null +++ b/batch-publish/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/batch-publish/gradlew b/batch-publish/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/batch-publish/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/batch-publish/gradlew.bat b/batch-publish/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/batch-publish/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/batch-publish/settings.gradle b/batch-publish/settings.gradle new file mode 100644 index 0000000..450b14f --- /dev/null +++ b/batch-publish/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'batch-publish' diff --git a/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java b/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java new file mode 100644 index 0000000..b6738ba --- /dev/null +++ b/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java @@ -0,0 +1,93 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.AckPolicy; +import io.nats.client.api.ConsumerConfiguration; +import io.nats.client.api.PublishAck; +import io.nats.client.api.StreamConfiguration; +import io.nats.client.impl.Headers; +import io.synadia.bp.BatchPublisher; + +public class BatchPublishExample { + static final String NATS_URL = "nats://localhost:4222"; + static final String STREAM = "bp-stream"; + static final String SUBJECT = "bp-subject"; + static final int BATCH_SIZE = 1000; // !!! MAX IS 1000 + static final int CONFIRM_EVERY = 50; + + public static void main(String[] args) throws Exception { + try (Connection nc = Nats.connect(NATS_URL)) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Set up a fresh counter stream + try { jsm.deleteStream(STREAM); } catch (JetStreamApiException ignore) {} + StreamConfiguration config = StreamConfiguration.builder() + .name(STREAM) + .subjects(SUBJECT) + .allowAtomicPublish() + .build(); + jsm.addStream(config); + + JetStream js = nc.jetStream(); + + BatchPublisher publisher = new BatchPublisher(nc); + publisher.open(); + + for (int i = 1; i < BATCH_SIZE; i++) { + Headers h = new Headers(); + h.put("my-id", "" + i); + if (CONFIRM_EVERY > 0 && i % CONFIRM_EVERY == 0) { + publisher.publishConfirm(SUBJECT, h, ("data-" + i).getBytes()); + System.out.println("Batch In Progress Confirmed at " + i); + } + else { + publisher.publish(SUBJECT, h, ("data-" + i).getBytes()); + } + } + + Headers h = new Headers(); + h.put("my-id", "" + BATCH_SIZE); + PublishAck pa = publisher.publishLast(SUBJECT, h, ("data-" + BATCH_SIZE).getBytes()); + assert pa.getJv() != null; + System.out.println("Commit Ack: " + pa.getJv().toJson()); + + // simple subscript + JetStreamSubscription sub = js.subscribe(SUBJECT, PushSubscribeOptions.builder() + .configuration(ConsumerConfiguration.builder() + .filterSubject(SUBJECT) + .ackPolicy(AckPolicy.None) + .build()) + .build()); + int count = 0; + Message m = sub.nextMessage(500); + while (m != null) { + count++; + m = sub.nextMessage(50); + } + System.out.println("Retrieved " + count + " messages."); + } + } + + public static String toString(Message msg) { + StringBuilder sb = new StringBuilder(System.lineSeparator()) + .append(" Subject: ").append(msg.getSubject()); + if (msg.getData() == null || msg.getData().length == 0) { + sb.append(" | No Data"); + } + else { + sb.append(" | Data: ").append(new String(msg.getData())); + } + Headers h = msg.getHeaders(); + if (h != null && !h.isEmpty()) { + sb.append(System.lineSeparator()).append(" Headers:"); + for (String key : h.keySet()) { + sb.append(System.lineSeparator()).append(" "); + sb.append(key).append("=").append(h.get(key)); + } + } + return sb.toString(); + } +} diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java new file mode 100644 index 0000000..418d211 --- /dev/null +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java @@ -0,0 +1,10 @@ +// Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.bp; + +public class BatchPublishException extends Exception { + public BatchPublishException(Throwable cause) { + super(cause); + } +} diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java new file mode 100644 index 0000000..a1cd0af --- /dev/null +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java @@ -0,0 +1,146 @@ +// Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.bp; + +import io.nats.client.*; +import io.nats.client.api.PublishAck; +import io.nats.client.impl.Headers; +import io.nats.client.support.NatsJetStreamConstants; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static io.nats.client.support.Validator.validateNotNull; + +public class BatchPublisher { + private final Connection conn; + private final Duration requestTimeout; + private int lastSeq; + private Headers openedHeaders; + private List lastUserHeaderKeys; + + /** + * Construct a BatchPublisher instance. + * @param conn the connection to operate under + */ + public BatchPublisher(Connection conn) { + this(conn, conn.getOptions().getConnectionTimeout()); + } + + /** + * Construct a BatchPublisher instance. + * @param conn the connection to operate under + * @param jso a JetStreamOptions instance to extract the request timeout + */ + public BatchPublisher(Connection conn, JetStreamOptions jso) { + this(conn, jso.getRequestTimeout()); + } + + public BatchPublisher(Connection conn, Duration requestTimeout) { + validateNotNull(conn, "Connection required,"); + if (!conn.getServerInfo().isNewerVersionThan("2.11.99")) { + throw new IllegalArgumentException("Batch direct get not available until server version 2.11.0."); + } + this.conn = conn; + this.requestTimeout = requestTimeout; + lastSeq = 0; + } + + public void open() { + _open(new NUID().next()); + } + + public void open(String batchId) { + validateNotNull(batchId, "Batch ID"); + _open(batchId); + } + + private void _open(String batchId) { + lastSeq = 0; + openedHeaders = new Headers(); + openedHeaders.put(NatsJetStreamConstants.NATS_BATCH_ID_HDR, batchId); + lastUserHeaderKeys = new ArrayList<>(); + } + + public void publish(String subject, byte[] data) { + publish(subject, null, data); + } + + public void publish(String subject, Headers userHeaders, byte[] data) { + if (openedHeaders == null) { + throw new IllegalStateException("Batch not opened"); + } + updateHeaders(userHeaders, false); + conn.publish(subject, openedHeaders, data); + } + + public boolean publishConfirm(String subject, byte[] data) throws BatchPublishException { + request(subject, null, data, false); + return true; + } + + public boolean publishConfirm(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + request(subject, userHeaders, data, false); + return true; + } + + public PublishAck publishLast(String subject, byte[] data) throws BatchPublishException { + return publishLast(subject, null, data); + } + + public PublishAck publishLast(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + try { + return new PublishAck(request(subject, userHeaders, data, true)); + } + catch (JetStreamApiException | IOException e) { + throw new BatchPublishException(e); + } + finally { + openedHeaders = null; // closes the batch + } + } + + private Message request(String subject, Headers userHeaders, byte[] data, boolean commit) throws BatchPublishException { + if (openedHeaders == null) { + throw new IllegalStateException("Batch not opened"); + } + try { + updateHeaders(userHeaders, commit); + CompletableFuture f = conn.requestWithTimeout(subject, openedHeaders, data, requestTimeout); + return f.get(requestTimeout.toNanos(), TimeUnit.NANOSECONDS); + } + catch (ExecutionException | TimeoutException e) { + throw new BatchPublishException(e); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new BatchPublishException(e); + } + } + + private void updateHeaders(Headers userHeaders, boolean commit) { + if (!lastUserHeaderKeys.isEmpty()) { + openedHeaders.remove(lastUserHeaderKeys); + lastUserHeaderKeys.clear(); + } + if (userHeaders != null && !userHeaders.isEmpty()) { + Set keys = userHeaders.keySet(); + for (String key : keys) { + openedHeaders.add(key, userHeaders.get(key)); + lastUserHeaderKeys.add(key); + } + } + openedHeaders.put(NatsJetStreamConstants.NATS_BATCH_SEQUENCE_HDR, Integer.toString(++lastSeq)); + if (commit) { + openedHeaders.put(NatsJetStreamConstants.NATS_BATCH_COMMIT_HDR, "1"); + } + } +} diff --git a/batch-publish/src/main/javadoc/images/favicon.ico b/batch-publish/src/main/javadoc/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9464855b4b53e64a0b464a138e21734ece9a878b GIT binary patch literal 1150 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 + + + + +Synadia's Batch Publish +

Synadia Logo

+ + + + diff --git a/batch-publish/src/main/resources/placeholder.txt b/batch-publish/src/main/resources/placeholder.txt new file mode 100644 index 0000000..0394bc8 --- /dev/null +++ b/batch-publish/src/main/resources/placeholder.txt @@ -0,0 +1,2 @@ +This is just a placeholder. +Placeholder updated 05/18/2025 to force build \ No newline at end of file diff --git a/batch-publish/src/test/java/io/nats/client/support/Debug.java b/batch-publish/src/test/java/io/nats/client/support/Debug.java new file mode 100644 index 0000000..cada269 --- /dev/null +++ b/batch-publish/src/test/java/io/nats/client/support/Debug.java @@ -0,0 +1,461 @@ +package io.nats.client.support; + +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.Message; +import io.nats.client.api.ConsumerConfiguration; +import io.nats.client.api.ConsumerInfo; +import io.nats.client.api.SequenceInfo; +import io.nats.client.api.StreamInfo; +import io.nats.client.impl.Headers; +import io.nats.client.impl.NatsJetStreamMetaData; +import io.nats.client.impl.NatsMessage; + +import java.io.IOException; +import java.time.ZonedDateTime; +import java.util.List; + +import static java.nio.charset.StandardCharsets.UTF_8; + +// MODIFIED 2/10/2025 + +@SuppressWarnings("SameParameterValue") +public abstract class Debug { + + public interface DebugPrinter { + void println(String s); + } + + public static final String SEP = " | "; + public static final String DIV = "/"; + public static final String PAD = " "; + public static final String REPLACE = "\\Q%s\\E"; + public static boolean DO_NOT_TRUNCATE = true; + public static boolean PRINT_THREAD_ID = true; + public static boolean PRINT_TIME = true; + public static boolean PAUSE = false; + public static DebugPrinter DEBUG_PRINTER = System.out::println; + public static int MAX_DATA_DISPLAY = 50; + + private Debug() {} /* ensures cannot be constructed */ + + public static void msg(Message msg) { + info(null, msg, true, null); + } + + public static void msg(Message msg, Object... extras) { + info(null, msg, true, stringify(extras, false)); + } + + public static void msg(String label, Message msg, Object... extras) { + info(label, msg, true, stringify(extras, false)); + } + + public static void stackTrace(String label) { + if (PAUSE) { return; } + try { + throw new Exception(); + } + catch (Exception e) { + stackTrace(label, e); + } + } + + public static void stackTrace(String label, Throwable t) { + if (PAUSE) { return; } + String m = t.getMessage(); + if (m == null) { + info(label, "Stack Trace"); + } + else { + info(label, "Stack Trace", t.getMessage()); + } + boolean compress = false; + StackTraceElement[] elements = t.getStackTrace(); + for (int i = 0; i < elements.length; i++) { + String ts = elements[i].toString(); + if (i > 0) { + if (ts.startsWith("io.nats")) { + if (compress) { + info(label, "> ..."); + } + info(label, "> " + ts); + compress = false; + } + else { + compress = true; + } + } + if (ts.startsWith("java")) { + break; + } + } + if (compress) { + info(label, "> ..."); + } + } + + public static void info(String label, Object... extras) { + if (PAUSE) { return; } + if (extras == null || extras.length == 0) { + info(label, null, false, null); + } + else if (extras[0] instanceof NatsMessage) { + info(label, (NatsMessage)extras[0], true, stringify(extras, true)); + } + else { + info(label, null, false, stringify(extras, false)); + } + } + + public static void info(String label, Message msg, boolean forMsg, String extra) { + if (PAUSE) { return; } + String start; + if (PRINT_TIME && PRINT_THREAD_ID) { + start = "[" + Thread.currentThread().getName() + "@" + time() + "] "; + } + else if (PRINT_TIME){ + start = "[" + time() + "] "; + } + else if (PRINT_THREAD_ID){ + start = "[" + Thread.currentThread().getName() + "] "; + } + else { + start = ""; + } + + if (extra == null) { + extra = ""; + } + else { + extra = SEP + extra; + } + + if (label != null) { + label = label.trim(); + } + if (label == null || label.isEmpty()) { + label = start; + } + else { + label = start + label; + } + + if (msg == null) { + if (forMsg) { + DEBUG_PRINTER.println(label + "" + extra); + } + else { + DEBUG_PRINTER.println(label + extra); + } + return; + } + + if (msg.isStatusMessage()) { + DEBUG_PRINTER.println(label + sidString(msg) + msgInfoString(msg) + msg.getStatus() + extra); + } + else if (msg.isJetStream()) { + DEBUG_PRINTER.println(label + sidString(msg) + msgInfoString(msg) + dataString(msg) + replyToString(msg) + extra); + } + else if (msg.getSubject() == null) { + DEBUG_PRINTER.println(label + sidString(msg) + msg + extra); + } + else { + DEBUG_PRINTER.println(label + sidString(msg) + msgInfoString(msg) + dataString(msg) + replyToString(msg) + extra); + } + debugHdr(label.length() + 1, msg); + } + + private static String messageString(Message msg) { + return sidString(msg) + msgInfoString(msg) + dataString(msg) + replyToString(msg); + } + + public static void warn(String label, Object... extras) { + info(label, extras); + } + + public static void warn(String label, Message msg, boolean forMsg, String extra) { + info(label, msg, forMsg, extra); + } + + public static String sidString(Message msg) { + return msg.getSID() == null ? SEP : " sid:" + msg.getSID() + SEP; + } + + public static String msgInfoString(Message msg) { + if (msg.isJetStream()) { + return msg.metaData().streamSequence() + + DIV + msg.metaData().consumerSequence() + + SEP + msg.getSubject() + + SEP; + } + return msg.getSubject() + SEP; + } + + public static String replyToString(Message msg) { + if (msg.isJetStream()) { + NatsJetStreamMetaData meta = msg.metaData(); + return "ss:" + meta.streamSequence() + ' ' + + "cc:" + meta.consumerSequence() + ' ' + + "dlvr:" + meta.deliveredCount() + ' ' + + "pnd:" + meta.pendingCount() + + SEP; + } + if (msg.getReplyTo() == null) { + return ""; + } + return msg.getReplyTo(); + } + + public static String time() { + return "" + System.currentTimeMillis(); + } + + public static String dataString(Message msg) { + byte[] data = msg.getData(); + if (data == null || data.length == 0) { + return "" + SEP; + } + String s = new String(data, UTF_8); + if (DO_NOT_TRUNCATE) { + return s + SEP; + } + + int at = s.indexOf("io.nats.jetstream.api"); + if (at == -1) { + return s.length() > MAX_DATA_DISPLAY ? s.substring(0, MAX_DATA_DISPLAY) + "..." : s; + } + int at2 = s.indexOf('"', at); + return s.substring(at, at2) + SEP; + } + + public static String stringify(Object[] extras, boolean skipFirst) { + if (extras == null || extras.length == 0) { + return null; + } + + if (extras.length == 1) { + return skipFirst ? null : getString(extras[0]); + } + + boolean notFirst = false; + StringBuilder sb = new StringBuilder(); + for (int i = (skipFirst ? 1 : 0); i < extras.length; i++) { + if (notFirst) { + sb.append(SEP); + } + else { + notFirst = true; + } + + String xtra = getString(extras[i]); + while (xtra.contains("%s")) { + xtra = xtra.replaceFirst(REPLACE, getString(extras[++i])); + } + sb.append(xtra); + } + + return sb.length() == 0 ? null : sb.toString(); + } + + public static String getString(Object o) { + if (o == null) { + return "null"; + } + if (o instanceof Message) { + Message msg = (Message)o; + if (msg.getSubject() == null) { + return msg.toString(); + } + return msgInfoString(msg) + dataString(msg) + replyToString(msg); + } +// if (o instanceof ConsumerInfo) { +// o = ((ConsumerInfo)o).getConsumerConfiguration(); +// } + if (o instanceof ConsumerInfo) { + return consumerInfoString((ConsumerInfo)o); + } + if (o instanceof SequenceInfo) { + return sequenceInfoString((SequenceInfo)o); + } + if (o instanceof NatsJetStreamMetaData) { + return metaDataString((NatsJetStreamMetaData)o); + } + if (o instanceof ZonedDateTime) { + return DateTimeUtils.toRfc3339((ZonedDateTime)o); + } +// if (o instanceof ZonedDateTime) { +// return zdtString((ZonedDateTime)o); +// } + if (o instanceof ConsumerConfiguration) { + return formatted((ConsumerConfiguration)o); + } + if (o instanceof Headers) { + Headers h = (Headers)o; + boolean notFirst = false; + StringBuilder sb = new StringBuilder("["); + for (String key : h.keySet()) { + if (notFirst) { + sb.append(','); + } + else { + notFirst = true; + } + sb.append(key).append("=").append(h.get(key)); + } + return sb.append(']').toString(); + } + if (o instanceof JsonSerializable) { + return ((JsonSerializable)o).toJson(); + } + if (o instanceof byte[]) { + byte[] bytes = (byte[])o; + if (bytes.length == 0) { + return ""; + } + return new String((byte[])o); + } + if (o instanceof String[]) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String s : (String[])o) { + if (first) { + first = false; + } + else { + sb.append(", "); + } + sb.append('\'') + .append(s == null ? "" : (s.isEmpty() ? "" : s)) + .append('\''); + } + return sb.toString(); + } + String s = o.toString(); + return s.isEmpty() ? "" : s; + } + + public static void debugHdr(int indent, Message msg) { + Headers h = msg.getHeaders(); + if (h != null && !h.isEmpty()) { + String pad = PAD.substring(0, indent); + for (String key : h.keySet()) { + DEBUG_PRINTER.println(pad + key + "=" + h.get(key)); + } + } + } + + public static void streamAndConsumer(Connection nc, String stream, String conName) throws IOException, JetStreamApiException { + streamAndConsumer(nc.jetStreamManagement(), stream, conName); + } + + public static void streamAndConsumer(JetStreamManagement jsm, String stream, String conName) throws IOException, JetStreamApiException { + printStreamInfo(jsm.getStreamInfo(stream)); + printConsumerInfo(jsm.getConsumerInfo(stream, conName)); + } + + public static void consumer(Connection nc, String stream, String conName) throws IOException, JetStreamApiException { + consumer(nc.jetStreamManagement(), stream, conName); + } + + public static void consumer(JetStreamManagement jsm, String stream, String conName) throws IOException, JetStreamApiException { + ConsumerInfo ci = jsm.getConsumerInfo(stream, conName); + DEBUG_PRINTER.println("Consumer pending=" + ci.getNumPending() + " waiting=" + ci.getNumWaiting() + " ackPending=" + ci.getNumAckPending()); + } + + public static void printStreamInfo(StreamInfo si) { + printObject(si, "StreamConfiguration", "StreamState", "ClusterInfo", "Mirror", "subjects", "sources"); + } + + public static void printStreamInfoList(List list) { + printObject(list, "!StreamInfo", "StreamConfiguration", "StreamState"); + } + + public static void printConsumerInfo(ConsumerInfo ci) { + printObject(ci, "ConsumerConfiguration", "Delivered", "AckFloor"); + } + + public static void printConsumerInfoList(List list) { + printObject(list, "!ConsumerInfo", "ConsumerConfiguration", "Delivered", "AckFloor"); + } + + public static void printObject(Object o, String... subObjectNames) { + String s = o.toString(); + for (String sub : subObjectNames) { + boolean noIndent = sub.startsWith("!"); + String sb = noIndent ? sub.substring(1) : sub; + String rx1 = ", " + sb; + String repl1 = (noIndent ? ",\n": ",\n ") + sb; + s = s.replace(rx1, repl1); + } + DEBUG_PRINTER.println(s); + } + + public static String pad2(int n) { + return n < 10 ? " " + n : "" + n; + } + + public static String pad3(int n) { + return n < 10 ? " " + n : (n < 100 ? " " + n : "" + n); + } + + public static String pad3z(int n) { + return n < 10 ? "00" + n : (n < 100 ? "0" + n : "" + n); + } + + public static String yn(boolean b) { + return b ? "Yes" : "No "; + } + + public static String FN = "\n "; + public static String FBN = "{\n "; + public static String formatted(JsonSerializable j) { + return j.getClass().getSimpleName() + j.toJson() + .replace("{\"", FBN + "\"").replace(",", "," + FN); + } + + public static String formatted(Object o) { + return formatted(o.toString()); + } + + public static String formatted(String s) { + return s.replace("{", FBN).replace(", ", "," + FN); + } + + public static String consumerInfoString(ConsumerInfo ci) { + return ci == null ? "null" : + "Consumer{" + + "pending=" + ci.getNumPending() + + ", waiting=" + ci.getNumWaiting() + + ", ackPending=" + ci.getNumAckPending() + + ", redelivered=" + ci.getRedelivered() + + ", delivered=" + sequenceInfoString(ci.getDelivered()) + + ", ackFloor=" + sequenceInfoString(ci.getAckFloor()) + + "} "; + } + + public static String sequenceInfoString(SequenceInfo si) { + return si == null ? "null" : + "{" + + "consumerSeq=" + si.getConsumerSequence() + + ", streamSeq=" + si.getStreamSequence() + + ", lastActive=" + zdtString(si.getLastActive()) + + '}'; + } + + public static String metaDataString(NatsJetStreamMetaData meta) { + return meta == null ? "null" : + "Meta{" + + "delivered=" + meta.deliveredCount() + + ", streamSeq=" + meta.streamSequence() + + ", consumerSeq=" + meta.consumerSequence() + + ", pending=" + meta.pendingCount() + + ", timestamp=" + zdtString(meta.timestamp()) + + '}'; + } + + public static String zdtString(ZonedDateTime zdt) { + return zdt == null ? "null" : zdt.toLocalTime().toString(); + } +} diff --git a/batch-publish/src/test/resources/placeholder.txt b/batch-publish/src/test/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/batch-publish/src/test/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java index 3704083..f87788c 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -17,9 +17,10 @@ import java.util.concurrent.TimeUnit; public class CounterContextExample { + static final String NATS_URL = "nats://localhost:4222"; public static void main(String[] args) throws Exception { - try (Connection nc = Nats.connect()) { + try (Connection nc = Nats.connect(NATS_URL)) { JetStreamManagement jsm = nc.jetStreamManagement(); // Set up a fresh counter stream diff --git a/counter/src/test/java/io/synadia/counter/CounterTests.java b/counter/src/test/java/io/synadia/counter/CounterTests.java index e093e69..724d8ae 100644 --- a/counter/src/test/java/io/synadia/counter/CounterTests.java +++ b/counter/src/test/java/io/synadia/counter/CounterTests.java @@ -124,17 +124,6 @@ public void testCounterBasics() throws Exception { assertEquals(1000, counter.get(subject3).intValue()); BigInteger total = BigInteger.ZERO; - LinkedBlockingQueue vResponses = counter.getMultiple(subject1, subject2, subject3); - CounterValueResponse vr = vResponses.poll(1, TimeUnit.SECONDS); - while (vr != null && vr.isValue()) { - total = total.add(vr.getValue()); - vr = vResponses.poll(10, TimeUnit.MILLISECONDS); - } - assertNotNull(vr); - assertTrue(vr.isEobStatus()); - assertEquals(1110, total.intValue()); - - total = BigInteger.ZERO; LinkedBlockingQueue eResponses = counter.getEntries(subject1, subject2, subject3); CounterEntryResponse er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { @@ -147,17 +136,6 @@ public void testCounterBasics() throws Exception { assertTrue(er.isEobStatus()); assertEquals(1110, total.intValue()); - total = BigInteger.ZERO; - vResponses = counter.getMultiple(subjectPrefix + ".*"); - vr = vResponses.poll(1, TimeUnit.SECONDS); - while (vr != null && vr.isValue()) { - total = total.add(vr.getValue()); - vr = vResponses.poll(10, TimeUnit.MILLISECONDS); - } - assertNotNull(vr); - assertTrue(vr.isEobStatus()); - assertEquals(1110, total.intValue()); - total = BigInteger.ZERO; eResponses = counter.getEntries(subjectPrefix + ".*"); er = eResponses.poll(1, TimeUnit.SECONDS); From e40bc285818a186a3eadc66ef249ed3b7066b588 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 18 Sep 2025 14:46:09 -0400 Subject: [PATCH 040/135] Initial Batch Publish --- .../synadia/examples/BatchPublishExample.java | 34 +++++++++++-------- .../java/io/synadia/bp/BatchPublisher.java | 23 ++++++++++--- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java b/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java index b6738ba..f2b1776 100644 --- a/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java +++ b/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java @@ -16,7 +16,7 @@ public class BatchPublishExample { static final String STREAM = "bp-stream"; static final String SUBJECT = "bp-subject"; static final int BATCH_SIZE = 1000; // !!! MAX IS 1000 - static final int CONFIRM_EVERY = 50; + static final int CONFIRM_EVERY = 100; public static void main(String[] args) throws Exception { try (Connection nc = Nats.connect(NATS_URL)) { @@ -34,27 +34,31 @@ public static void main(String[] args) throws Exception { JetStream js = nc.jetStream(); BatchPublisher publisher = new BatchPublisher(nc); - publisher.open(); - for (int i = 1; i < BATCH_SIZE; i++) { + for (int i = 1; i <= BATCH_SIZE; i++) { Headers h = new Headers(); - h.put("my-id", "" + i); - if (CONFIRM_EVERY > 0 && i % CONFIRM_EVERY == 0) { - publisher.publishConfirm(SUBJECT, h, ("data-" + i).getBytes()); - System.out.println("Batch In Progress Confirmed at " + i); + h.put("bp-id", "xyz-" + i); + byte[] data = ("data-" + i).getBytes(); + if (i == 1) { + publisher.open(SUBJECT, h, data); + System.out.println("Batch " + publisher.getBatchId() + " | Opened"); + } + else if (i == BATCH_SIZE) { + PublishAck pa = publisher.publishLast(SUBJECT, h, data); + assert pa.getJv() != null; + System.out.println("Batch " + pa.getBatchId() + " | Committed " + pa.getJv().toJson()); + } + else if (CONFIRM_EVERY > 0 && i % CONFIRM_EVERY == 0) { + if (publisher.publishConfirm(SUBJECT, h, data)) { + System.out.println("Batch " + publisher.getBatchId() + " | Progress confirmed at message " + i); + } } else { - publisher.publish(SUBJECT, h, ("data-" + i).getBytes()); + publisher.publish(SUBJECT, h, data); } } - Headers h = new Headers(); - h.put("my-id", "" + BATCH_SIZE); - PublishAck pa = publisher.publishLast(SUBJECT, h, ("data-" + BATCH_SIZE).getBytes()); - assert pa.getJv() != null; - System.out.println("Commit Ack: " + pa.getJv().toJson()); - - // simple subscript + // simple subscription JetStreamSubscription sub = js.subscribe(SUBJECT, PushSubscribeOptions.builder() .configuration(ConsumerConfiguration.builder() .filterSubject(SUBJECT) diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java index a1cd0af..dbf627c 100644 --- a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java @@ -54,20 +54,33 @@ public BatchPublisher(Connection conn, Duration requestTimeout) { lastSeq = 0; } - public void open() { - _open(new NUID().next()); + public String getBatchId() { + if (openedHeaders == null) { + return null; + } + return openedHeaders.getFirst(NatsJetStreamConstants.NATS_BATCH_ID_HDR); + } + + public boolean open(String subject, byte[] data) throws BatchPublishException { + return open(new NUID().next(), subject, null, data); } - public void open(String batchId) { + public boolean open(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + return open(new NUID().next(), subject, null, data); + } + + public boolean open(String batchId, String subject, byte[] data) throws BatchPublishException { validateNotNull(batchId, "Batch ID"); - _open(batchId); + return open(new NUID().next(), subject, null, data); } - private void _open(String batchId) { + public boolean open(String batchId, String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + validateNotNull(batchId, "Batch ID"); lastSeq = 0; openedHeaders = new Headers(); openedHeaders.put(NatsJetStreamConstants.NATS_BATCH_ID_HDR, batchId); lastUserHeaderKeys = new ArrayList<>(); + return publishConfirm(subject, userHeaders, data); } public void publish(String subject, byte[] data) { From 4d18ad619d40030ee63e9b3fceefd2ab907650cc Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 20 Sep 2025 16:16:58 -0400 Subject: [PATCH 041/135] Batch Publish Progress --- .../synadia/examples/BatchPublishExample.java | 25 ++- .../java/io/synadia/bp/BatchPublisher.java | 173 ++++++++++++------ ...ontextExample.java => CounterExample.java} | 2 +- 3 files changed, 125 insertions(+), 75 deletions(-) rename counter/src/examples/java/io/synadia/examples/{CounterContextExample.java => CounterExample.java} (99%) diff --git a/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java b/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java index f2b1776..af44bf1 100644 --- a/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java +++ b/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java @@ -33,28 +33,27 @@ public static void main(String[] args) throws Exception { JetStream js = nc.jetStream(); - BatchPublisher publisher = new BatchPublisher(nc); + BatchPublisher publisher = BatchPublisher.builder() + .connection(nc) + .batchId(NUID.nextGlobal()) + .build(); for (int i = 1; i <= BATCH_SIZE; i++) { Headers h = new Headers(); - h.put("bp-id", "xyz-" + i); + h.put("my-id", "xyz-" + i); byte[] data = ("data-" + i).getBytes(); - if (i == 1) { - publisher.open(SUBJECT, h, data); - System.out.println("Batch " + publisher.getBatchId() + " | Opened"); - } - else if (i == BATCH_SIZE) { - PublishAck pa = publisher.publishLast(SUBJECT, h, data); + if (i == BATCH_SIZE) { + System.out.println("Commit"); + PublishAck pa = publisher.commit(SUBJECT, h, data); assert pa.getJv() != null; - System.out.println("Batch " + pa.getBatchId() + " | Committed " + pa.getJv().toJson()); + System.out.println("Batch [" + pa.getBatchId() + "] Committed " + pa.getJv().toJson()); } else if (CONFIRM_EVERY > 0 && i % CONFIRM_EVERY == 0) { - if (publisher.publishConfirm(SUBJECT, h, data)) { - System.out.println("Batch " + publisher.getBatchId() + " | Progress confirmed at message " + i); - } + publisher.addWithConfirm(SUBJECT, h, data); + System.out.println("Batch [" + publisher.getBatchId() + "] Progress confirmed at message " + i); } else { - publisher.publish(SUBJECT, h, data); + publisher.add(SUBJECT, h, data); } } diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java index dbf627c..d888c4e 100644 --- a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java @@ -3,10 +3,15 @@ package io.synadia.bp; -import io.nats.client.*; +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.Message; +import io.nats.client.NUID; import io.nats.client.api.PublishAck; import io.nats.client.impl.Headers; import io.nats.client.support.NatsJetStreamConstants; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.time.Duration; @@ -18,98 +23,144 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static io.nats.client.support.Validator.emptyAsNull; import static io.nats.client.support.Validator.validateNotNull; public class BatchPublisher { + enum State { + Open, Closed, Discarded + } + private final Connection conn; private final Duration requestTimeout; + private final Headers headers; + private final List lastUserHeaderKeys; + private final String batchId; + private int lastSeq; - private Headers openedHeaders; - private List lastUserHeaderKeys; + private State state; - /** - * Construct a BatchPublisher instance. - * @param conn the connection to operate under - */ - public BatchPublisher(Connection conn) { - this(conn, conn.getOptions().getConnectionTimeout()); + + public static Builder builder() { + return new Builder(); } /** - * Construct a BatchPublisher instance. - * @param conn the connection to operate under - * @param jso a JetStreamOptions instance to extract the request timeout + * The builder class for the BatchPublisher */ - public BatchPublisher(Connection conn, JetStreamOptions jso) { - this(conn, jso.getRequestTimeout()); - } + public static class Builder { + private Connection conn; + private Duration requestTimeout; + private String batchId; + + public Builder connection(Connection conn) { + this.conn = conn; + return this; + } - public BatchPublisher(Connection conn, Duration requestTimeout) { - validateNotNull(conn, "Connection required,"); - if (!conn.getServerInfo().isNewerVersionThan("2.11.99")) { - throw new IllegalArgumentException("Batch direct get not available until server version 2.11.0."); + public Builder requestTimeout(Duration requestTimeout) { + this.requestTimeout = requestTimeout; + return this; + } + + public Builder batchId(String batchId) { + this.batchId = batchId; + return this; + } + + public BatchPublisher build() { + validateNotNull(conn, "Connection required,"); + if (!conn.getServerInfo().isNewerVersionThan("2.11.99")) { + throw new IllegalArgumentException("Batch direct get not available until server version 2.11.0."); + } + if (requestTimeout == null) { + requestTimeout = conn.getOptions().getConnectionTimeout(); + } + batchId = emptyAsNull(batchId); + if (batchId == null) { + batchId = new NUID().next(); + } + else if (batchId.length() > 64){ + throw new IllegalArgumentException("Batch ID cannot be longer than 64 characters"); + } + return new BatchPublisher(this); } - this.conn = conn; - this.requestTimeout = requestTimeout; + } + + private BatchPublisher(BatchPublisher.Builder b) { + conn = b.conn; + requestTimeout = b.requestTimeout; + batchId = b.batchId; + headers = new Headers(); + headers.put(NatsJetStreamConstants.NATS_BATCH_ID_HDR, batchId); + lastUserHeaderKeys = new ArrayList<>(); lastSeq = 0; + state = State.Open; } + @NonNull + public Duration getRequestTimeout() { + return requestTimeout; + } + + @Nullable public String getBatchId() { - if (openedHeaders == null) { - return null; - } - return openedHeaders.getFirst(NatsJetStreamConstants.NATS_BATCH_ID_HDR); + return batchId; } - public boolean open(String subject, byte[] data) throws BatchPublishException { - return open(new NUID().next(), subject, null, data); + public int size() { + return lastSeq; } - public boolean open(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { - return open(new NUID().next(), subject, null, data); + public void discard() { + state = State.Discarded; } - public boolean open(String batchId, String subject, byte[] data) throws BatchPublishException { - validateNotNull(batchId, "Batch ID"); - return open(new NUID().next(), subject, null, data); + public boolean isOpen() { + return state == State.Open; } - public boolean open(String batchId, String subject, Headers userHeaders, byte[] data) throws BatchPublishException { - validateNotNull(batchId, "Batch ID"); - lastSeq = 0; - openedHeaders = new Headers(); - openedHeaders.put(NatsJetStreamConstants.NATS_BATCH_ID_HDR, batchId); - lastUserHeaderKeys = new ArrayList<>(); - return publishConfirm(subject, userHeaders, data); + public boolean isDiscarded() { + return state == State.Discarded; } - public void publish(String subject, byte[] data) { - publish(subject, null, data); + public boolean isClosed() { + return state == State.Closed; } - public void publish(String subject, Headers userHeaders, byte[] data) { - if (openedHeaders == null) { - throw new IllegalStateException("Batch not opened"); + public void add(String subject, byte[] data) throws BatchPublishException { + add(subject, null, data); + } + + public void add(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + if (state != State.Open) { + throw new IllegalStateException("Batch not open: " + state); + } + if (lastSeq == 0) { // first publish + request(subject, userHeaders, data, false); + } + else { + updateHeaders(userHeaders, false); + conn.publish(subject, headers, data); } - updateHeaders(userHeaders, false); - conn.publish(subject, openedHeaders, data); } - public boolean publishConfirm(String subject, byte[] data) throws BatchPublishException { - request(subject, null, data, false); - return true; + public void addWithConfirm(String subject, byte[] data) throws BatchPublishException { + addWithConfirm(subject, null, data); } - public boolean publishConfirm(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + public void addWithConfirm(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + if (state != State.Open) { + throw new IllegalStateException("Batch not open: " + state); + } request(subject, userHeaders, data, false); - return true; } - public PublishAck publishLast(String subject, byte[] data) throws BatchPublishException { - return publishLast(subject, null, data); + public PublishAck commit(String subject, byte[] data) throws BatchPublishException { + return commit(subject, null, data); } - public PublishAck publishLast(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + public PublishAck commit(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { try { return new PublishAck(request(subject, userHeaders, data, true)); } @@ -117,17 +168,17 @@ public PublishAck publishLast(String subject, Headers userHeaders, byte[] data) throw new BatchPublishException(e); } finally { - openedHeaders = null; // closes the batch + state = State.Closed; } } private Message request(String subject, Headers userHeaders, byte[] data, boolean commit) throws BatchPublishException { - if (openedHeaders == null) { + if (headers == null) { throw new IllegalStateException("Batch not opened"); } try { updateHeaders(userHeaders, commit); - CompletableFuture f = conn.requestWithTimeout(subject, openedHeaders, data, requestTimeout); + CompletableFuture f = conn.requestWithTimeout(subject, headers, data, requestTimeout); return f.get(requestTimeout.toNanos(), TimeUnit.NANOSECONDS); } catch (ExecutionException | TimeoutException e) { @@ -141,19 +192,19 @@ private Message request(String subject, Headers userHeaders, byte[] data, boolea private void updateHeaders(Headers userHeaders, boolean commit) { if (!lastUserHeaderKeys.isEmpty()) { - openedHeaders.remove(lastUserHeaderKeys); + headers.remove(lastUserHeaderKeys); lastUserHeaderKeys.clear(); } if (userHeaders != null && !userHeaders.isEmpty()) { Set keys = userHeaders.keySet(); for (String key : keys) { - openedHeaders.add(key, userHeaders.get(key)); + headers.add(key, userHeaders.get(key)); lastUserHeaderKeys.add(key); } } - openedHeaders.put(NatsJetStreamConstants.NATS_BATCH_SEQUENCE_HDR, Integer.toString(++lastSeq)); + headers.put(NatsJetStreamConstants.NATS_BATCH_SEQUENCE_HDR, Integer.toString(++lastSeq)); if (commit) { - openedHeaders.put(NatsJetStreamConstants.NATS_BATCH_COMMIT_HDR, "1"); + headers.put(NatsJetStreamConstants.NATS_BATCH_COMMIT_HDR, "1"); } } } diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterExample.java similarity index 99% rename from counter/src/examples/java/io/synadia/examples/CounterContextExample.java rename to counter/src/examples/java/io/synadia/examples/CounterExample.java index f87788c..a0fd427 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterExample.java @@ -16,7 +16,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -public class CounterContextExample { +public class CounterExample { static final String NATS_URL = "nats://localhost:4222"; public static void main(String[] args) throws Exception { From bed75fe9003d89f7b7842e6b0368c5d36c15ed33 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 25 Sep 2025 12:07:42 -0400 Subject: [PATCH 042/135] update jnats library --- chaos-runner/build.gradle | 2 +- counter/build.gradle | 2 +- direct-batch/build.gradle | 2 +- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- schedule-message/build.gradle | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 28dda4c..ddde221 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' // this is only for the example and the uber jar won't include it - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/counter/build.gradle b/counter/build.gradle index 25cb970..3539b52 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'io.synadia:direct-batch:0.1.3-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 1e19c4c..0e852e9 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index 93cfa46..d02096a 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index 2bfaf68..03ecce8 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index aec0f31..67a5aa0 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index 23b8239..bc5b905 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 0888169..953fb23 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'commons-codec:commons-codec:1.18.0' From 450e12502268c4fd71b0c76f58e94fb234a1eac4 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 25 Sep 2025 12:07:54 -0400 Subject: [PATCH 043/135] Batch Publish Examples --- batch-publish/build.gradle | 2 +- ...ple.java => BasicBatchPublishExample.java} | 25 +- .../ExpectationsBatchPublishExample.java | 103 +++++ .../io/synadia/bp/BatchPublishException.java | 51 ++- .../io/synadia/bp/BatchPublishOptions.java | 270 +++++++++++++ .../java/io/synadia/bp/BatchPublisher.java | 366 +++++++++++++----- 6 files changed, 704 insertions(+), 113 deletions(-) rename batch-publish/src/examples/java/io/synadia/examples/{BatchPublishExample.java => BasicBatchPublishExample.java} (81%) create mode 100644 batch-publish/src/examples/java/io/synadia/examples/ExpectationsBatchPublishExample.java create mode 100644 batch-publish/src/main/java/io/synadia/bp/BatchPublishOptions.java diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index 171e04d..cd6f795 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.22.1.2_12-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java similarity index 81% rename from batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java rename to batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java index af44bf1..ace04e8 100644 --- a/batch-publish/src/examples/java/io/synadia/examples/BatchPublishExample.java +++ b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java @@ -4,19 +4,17 @@ package io.synadia.examples; import io.nats.client.*; -import io.nats.client.api.AckPolicy; -import io.nats.client.api.ConsumerConfiguration; -import io.nats.client.api.PublishAck; -import io.nats.client.api.StreamConfiguration; +import io.nats.client.api.*; import io.nats.client.impl.Headers; import io.synadia.bp.BatchPublisher; -public class BatchPublishExample { +public class BasicBatchPublishExample { static final String NATS_URL = "nats://localhost:4222"; static final String STREAM = "bp-stream"; static final String SUBJECT = "bp-subject"; static final int BATCH_SIZE = 1000; // !!! MAX IS 1000 - static final int CONFIRM_EVERY = 100; + static final boolean ACK_FIRST = true; // default is true usually never change this. + static final int AUTO_ACK_EVERY = 100; // 0 or less means no auto ack public static void main(String[] args) throws Exception { try (Connection nc = Nats.connect(NATS_URL)) { @@ -36,27 +34,28 @@ public static void main(String[] args) throws Exception { BatchPublisher publisher = BatchPublisher.builder() .connection(nc) .batchId(NUID.nextGlobal()) + .ackFirst(ACK_FIRST) + .ackEvery(AUTO_ACK_EVERY) .build(); for (int i = 1; i <= BATCH_SIZE; i++) { Headers h = new Headers(); - h.put("my-id", "xyz-" + i); + h.put("my-header", "xyz-" + i); byte[] data = ("data-" + i).getBytes(); if (i == BATCH_SIZE) { - System.out.println("Commit"); PublishAck pa = publisher.commit(SUBJECT, h, data); assert pa.getJv() != null; System.out.println("Batch [" + pa.getBatchId() + "] Committed " + pa.getJv().toJson()); } - else if (CONFIRM_EVERY > 0 && i % CONFIRM_EVERY == 0) { - publisher.addWithConfirm(SUBJECT, h, data); - System.out.println("Batch [" + publisher.getBatchId() + "] Progress confirmed at message " + i); - } else { publisher.add(SUBJECT, h, data); } } + StreamInfo si = jsm.getStreamInfo(STREAM, StreamInfoOptions.allSubjects()); + long messages = si.getStreamState().getSubjectMap().get(SUBJECT); + System.out.println("Stream State shows '" + SUBJECT + "' has " + messages + " messages."); + // simple subscription JetStreamSubscription sub = js.subscribe(SUBJECT, PushSubscribeOptions.builder() .configuration(ConsumerConfiguration.builder() @@ -70,7 +69,7 @@ else if (CONFIRM_EVERY > 0 && i % CONFIRM_EVERY == 0) { count++; m = sub.nextMessage(50); } - System.out.println("Retrieved " + count + " messages."); + System.out.println("Consumed " + count + " messages from '" + SUBJECT + "'"); } } diff --git a/batch-publish/src/examples/java/io/synadia/examples/ExpectationsBatchPublishExample.java b/batch-publish/src/examples/java/io/synadia/examples/ExpectationsBatchPublishExample.java new file mode 100644 index 0000000..958d48d --- /dev/null +++ b/batch-publish/src/examples/java/io/synadia/examples/ExpectationsBatchPublishExample.java @@ -0,0 +1,103 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.*; +import io.nats.client.impl.Headers; +import io.synadia.bp.BatchPublishOptions; +import io.synadia.bp.BatchPublisher; + +public class ExpectationsBatchPublishExample { + static final String NATS_URL = "nats://localhost:4222"; + static final String STREAM = "expect-batch"; + static final String SUBJECT_PREFIX = "expect."; + static final String SUBJECTS = SUBJECT_PREFIX + ">"; + static final String SUBJECT_A = SUBJECT_PREFIX + "A"; + static final String SUBJECT_B = SUBJECT_PREFIX + "B"; + + public static void main(String[] args) throws Exception { + try (Connection nc = Nats.connect(NATS_URL)) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Set up a fresh counter stream + try { jsm.deleteStream(STREAM); } catch (JetStreamApiException ignore) {} + StreamConfiguration config = StreamConfiguration.builder() + .name(STREAM) + .subjects(SUBJECTS) + .allowAtomicPublish() + .build(); + jsm.addStream(config); + + JetStream js = nc.jetStream(); + + PublishAck paA = js.publish(SUBJECT_A, "A1".getBytes()); + System.out.println("Non-Batch Publish to '" + SUBJECT_A + "' --> " + paA); + + PublishAck paB = js.publish(SUBJECT_B, "B1".getBytes()); + System.out.println("Non-Batch Publish to '" + SUBJECT_B + "' --> " + paB); + + BatchPublisher publisher = BatchPublisher.builder() + .connection(nc) + .batchId("4273") + .build(); + + BatchPublishOptions.Builder bpoBuilder = BatchPublishOptions.builder() + .expectedLastSequence(paB.getSeqno()) + .expectedLastSubjectSequence(paA.getSeqno()) + .expectedLastSubjectSequenceSubject(SUBJECT_A); + + BatchPublishOptions bpOpts = bpoBuilder.build(); + System.out.println("Batch Add to '" + SUBJECT_A + "', 'A2'"); + publisher.add(SUBJECT_A, "A2".getBytes(), bpOpts); + + // demonstrates re-use of the builder + bpOpts = bpoBuilder.clearExpected() + .expectedLastSubjectSequence(paB.getSeqno()) + .expectedLastSubjectSequenceSubject(SUBJECT_B) + .build(); + System.out.println("Batch Add to '" + SUBJECT_B + "', 'B2'"); + publisher.add(SUBJECT_B, "B2".getBytes(), bpOpts); + + System.out.println("Batch Commit Add to '" + SUBJECT_A + "', 'A3'"); + PublishAck pa = publisher.commit(SUBJECT_A, "A3".getBytes()); + assert pa.getJv() != null; + System.out.println("Batch [" + pa.getBatchId() + "] Committed " + pa.getJv().toJson()); + + StreamInfo si = jsm.getStreamInfo(STREAM, StreamInfoOptions.allSubjects()); + System.out.println("Stream State"); + for (Subject subject : si.getStreamState().getSubjects()) { + System.out.println(" '" + subject.getName() + "' has " + subject.getCount() + " messages."); + } + + // simple subscription + JetStreamSubscription sub = js.subscribe(SUBJECTS, PushSubscribeOptions.builder() + .configuration(ConsumerConfiguration.builder() + .filterSubject(SUBJECTS) + .ackPolicy(AckPolicy.None) + .build()) + .build()); + System.out.println("Messages:"); + Message m = sub.nextMessage(500); + while (m != null) { + System.out.println(toString(m)); + m = sub.nextMessage(50); + } + } + } + + public static String toString(Message msg) { + StringBuilder sb = new StringBuilder(" '").append(msg.getSubject()); + sb.append("', '").append(new String(msg.getData())).append("'"); + Headers h = msg.getHeaders(); + if (h != null && !h.isEmpty()) { + sb.append(", Headers:"); + for (String key : h.keySet()) { + sb.append(System.lineSeparator()).append(" "); + sb.append(key).append("=").append(h.get(key)); + } + } + return sb.toString(); + } +} diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java index 418d211..1329231 100644 --- a/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java @@ -1,10 +1,59 @@ -// Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. package io.synadia.bp; +import io.nats.client.JetStreamApiException; +import org.jspecify.annotations.Nullable; + public class BatchPublishException extends Exception { + private final JetStreamApiException jsApiException; + + public BatchPublishException(String message) { + super(message); + jsApiException = null; + } + + public BatchPublishException(JetStreamApiException cause) { + super(cause); + jsApiException = cause; + } + public BatchPublishException(Throwable cause) { super(cause); + jsApiException = null; + } + + @Nullable + public JetStreamApiException getJsApiException() { + return jsApiException; + } + + /** + * Get the error code from the response if the exception is a JetStreamApiException + * otherwise will be -1 + * @return the code + */ + public int getErrorCode() { + return jsApiException == null ? -1 : jsApiException.getErrorCode(); + } + + /** + * Get the error code from the response if the exception is a JetStreamApiException + * otherwise will be -1 + * @return the code + */ + public int getApiErrorCode() { + return jsApiException == null ? -1 : jsApiException.getApiErrorCode(); + } + + /** + * Get the description from the response if the exception is a JetStreamApiException + * otherwise will be null + * @return the description + */ + @Nullable + public String getErrorDescription() { + return jsApiException == null ? null : jsApiException.getErrorDescription(); } } diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublishOptions.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublishOptions.java new file mode 100644 index 0000000..934ba30 --- /dev/null +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublishOptions.java @@ -0,0 +1,270 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.bp; + +import io.nats.client.MessageTtl; + +import java.time.Duration; + +import static io.nats.client.PublishOptions.DEFAULT_TIMEOUT; +import static io.nats.client.PublishOptions.UNSET_LAST_SEQUENCE; +import static io.nats.client.support.Validator.*; + +public class BatchPublishOptions { + public final String expectedStream; + public final long expectedLastSeq; + public final long expectedLastSubSeq; + public final String expectedLastSubSeqSubject; + public final MessageTtl messageTtl; + + private BatchPublishOptions(Builder b) { + this.expectedStream = b.expectedStream; + this.expectedLastSeq = b.expectedLastSeq; + this.expectedLastSubSeq = b.expectedLastSubSeq; + this.expectedLastSubSeqSubject = b.expectedLastSubSeqSubject; + this.messageTtl = b.messageTtl; + } + + @Override + public String toString() { + return "BatchPublishOptions{" + + ", expectedStream='" + expectedStream + '\'' + + ", expectedLastSeq=" + expectedLastSeq + + ", expectedLastSubSeq=" + expectedLastSubSeq + + ", expectedLastSubSeqSub=" + expectedLastSubSeqSubject + + ", messageTtl=" + getMessageTtl() + + '}'; + } + + /** + * Gets the expected stream. + * @return the stream. + */ + public String getExpectedStream() { + return expectedStream; + } + + /** + * Gets the expected last sequence number of the stream. + * @return sequence number + */ + public long getExpectedLastSequence() { + return expectedLastSeq; + } + + /** + * Gets the expected last subject sequence number of the stream. + * @return last subject sequence number + */ + public long getExpectedLastSubjectSequence() { + return expectedLastSubSeq; + } + + /** + * Gets the expected subject to limit last subject sequence number of the stream. + * @return the last subject sequence number's limit subject + */ + public String getExpectedLastSubjectSequenceSubject() { + return expectedLastSubSeqSubject; + } + + /** + * Gets the message ttl string. Might be null. Might be "never". + * 10 seconds would be "10s" for the server + * @return the message ttl string + */ + public String getMessageTtl() { + return messageTtl == null ? null : messageTtl.getTtlString(); + } + + /** + * Creates a builder for the options. + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * PublishOptions are created using a Builder. The builder supports chaining and will + * create a default set of options if no methods are calls. The builder can also + * be created from a properties object using the property names defined with the + * prefix PROP_ in this class. + */ + public static class Builder { + Duration ackTimeout = DEFAULT_TIMEOUT; + boolean ackFirst = true; + int ackEvery = 0; + String expectedStream; + long expectedLastSeq = UNSET_LAST_SEQUENCE; + long expectedLastSubSeq = UNSET_LAST_SEQUENCE; + String expectedLastSubSeqSubject; + MessageTtl messageTtl; + + /** + * Constructs a new publish options Builder with the default values. + */ + public Builder() {} + + /** + * Sets the timeout to wait for the acknowledgement for acks when adding or the commit. + * @param ackTimeout the ack timeout. + * @return The Builder + */ + public Builder ackTimeout(Duration ackTimeout) { + this.ackTimeout = validateDurationNotRequiredGtOrEqZero(ackTimeout, DEFAULT_TIMEOUT); + return this; + } + + /** + * Sets the timeout im milliseconds to wait for the acknowledgement for acks when adding or the commit. + * @param ackTimeoutMillis the ack timeout. + * @return The Builder + */ + public Builder ackTimeout(long ackTimeoutMillis) { + this.ackTimeout = ackTimeoutMillis < 1 ? DEFAULT_TIMEOUT : Duration.ofMillis(ackTimeoutMillis); + return this; + } + + /** + * Whether to ack the first message. Defaults to true + * @param ackFirst the flag + * @return The Builder + */ + public Builder ackFirst(boolean ackFirst) { + this.ackFirst = ackFirst; + return this; + } + + /** + * The interval to ack when adding a message, after the first message. Defaults to 0 (never). + * @param ackEvery the ack every value + * @return The Builder + */ + public Builder ackEvery(int ackEvery) { + this.ackEvery = ackEvery < 1 ? 0 : ackEvery; + return this; + } + + /** + * Sets the expected stream for the publish. If the + * stream does not match the server will not save the message. + * @param stream expected stream + * @return The Builder + */ + public Builder expectedStream(String stream) { + expectedStream = validateStreamName(stream, false); + return this; + } + + /** + * Sets the expected message sequence of the publish + * @param sequence the expected last sequence number + * @return The Builder + */ + public Builder expectedLastSequence(long sequence) { + // 0 has NO meaning to expectedLastSequence but we except 0 b/c the sequence is really a ulong + expectedLastSeq = validateGtEqMinus1(sequence, "Last Sequence"); + return this; + } + + /** + * Sets the expected subject message sequence of the publish + * @param sequence the expected last subject sequence number + * @return The Builder + */ + public Builder expectedLastSubjectSequence(long sequence) { + expectedLastSubSeq = validateGtEqMinus1(sequence, "Last Subject Sequence"); + return this; + } + + /** + * Sets the filter subject for the expected last subject sequence + * This can be used for a wildcard since it is used + * in place of the message subject along with expectedLastSubjectSequence + * @param expectedLastSubSeqSubject the filter subject + * @return The Builder + */ + public Builder expectedLastSubjectSequenceSubject(String expectedLastSubSeqSubject) { + this.expectedLastSubSeqSubject = expectedLastSubSeqSubject; + return this; + } + + /** + * Sets the TTL for this specific message to be published. + * Less than 1 has the effect of clearing the message ttl + * @param msgTtlSeconds the ttl in seconds + * @return The Builder + */ + public Builder messageTtlSeconds(int msgTtlSeconds) { + this.messageTtl = msgTtlSeconds < 1 ? null : MessageTtl.seconds(msgTtlSeconds); + return this; + } + + /** + * Sets the TTL for this specific message to be published. Use at your own risk. + * The current specification can be found here @see
JetStream Per-Message TTL + * Null or empty has the effect of clearing the message ttl + * @param msgTtlCustom the custom ttl string + * @return The Builder + */ + public Builder messageTtlCustom(String msgTtlCustom) { + this.messageTtl = nullOrEmpty(msgTtlCustom) ? null : MessageTtl.custom(msgTtlCustom); + return this; + } + + /** + * Sets the TTL for this specific message to be published and never be expired + * @return The Builder + */ + public Builder messageTtlNever() { + this.messageTtl = MessageTtl.never(); + return this; + } + + /** + * Sets the TTL for this specific message to be published + * @param messageTtl the message ttl instance + * @return The Builder + */ + public Builder messageTtl(MessageTtl messageTtl) { + this.messageTtl = messageTtl; + return this; + } + + /** + * Clears the expected so the build can be re-used. + * Clears the following fields: + *
    + *
  • expectedLastSequence
  • + *
  • expectedLastSubSeq
  • + *
  • expectedLastSubSeqSubject
  • + *
+ * Does not clear the following fields: + *
    + *
  • ackTimeout
  • + *
  • ackFirst
  • + *
  • ackEvery
  • + *
  • expectedStream
  • + *
  • messageTtl
  • + *
+ * @return The Builder + */ + public Builder clearExpected() { + expectedLastSeq = UNSET_LAST_SEQUENCE; + expectedLastSubSeq = UNSET_LAST_SEQUENCE; + expectedLastSubSeqSubject = null; + return this; + } + + /** + * Builds the publish options. + * @return publish options + */ + public BatchPublishOptions build() { + return new BatchPublishOptions(this); + } + } + +} diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java index d888c4e..d9278cf 100644 --- a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java @@ -1,12 +1,9 @@ -// Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. package io.synadia.bp; -import io.nats.client.Connection; -import io.nats.client.JetStreamApiException; -import io.nats.client.Message; -import io.nats.client.NUID; +import io.nats.client.*; import io.nats.client.api.PublishAck; import io.nats.client.impl.Headers; import io.nats.client.support.NatsJetStreamConstants; @@ -15,99 +12,72 @@ import java.io.IOException; import java.time.Duration; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static io.nats.client.support.Validator.emptyAsNull; -import static io.nats.client.support.Validator.validateNotNull; +import static io.nats.client.PublishOptions.DEFAULT_TIMEOUT; +import static io.nats.client.support.NatsJetStreamConstants.*; +import static io.nats.client.support.Validator.*; public class BatchPublisher { enum State { Open, Closed, Discarded } - private final Connection conn; - private final Duration requestTimeout; - private final Headers headers; - private final List lastUserHeaderKeys; private final String batchId; + private final Connection conn; + private final Duration ackTimeout; + private final boolean ackFirst; + private final int ackEvery; + private final MessageTtl messageTtl; + private final Headers headers; // final to be re-used/cleared private int lastSeq; private State state; - - public static Builder builder() { - return new Builder(); - } - - /** - * The builder class for the BatchPublisher - */ - public static class Builder { - private Connection conn; - private Duration requestTimeout; - private String batchId; - - public Builder connection(Connection conn) { - this.conn = conn; - return this; - } - - public Builder requestTimeout(Duration requestTimeout) { - this.requestTimeout = requestTimeout; - return this; - } - - public Builder batchId(String batchId) { - this.batchId = batchId; - return this; - } - - public BatchPublisher build() { - validateNotNull(conn, "Connection required,"); - if (!conn.getServerInfo().isNewerVersionThan("2.11.99")) { - throw new IllegalArgumentException("Batch direct get not available until server version 2.11.0."); - } - if (requestTimeout == null) { - requestTimeout = conn.getOptions().getConnectionTimeout(); - } - batchId = emptyAsNull(batchId); - if (batchId == null) { - batchId = new NUID().next(); - } - else if (batchId.length() > 64){ - throw new IllegalArgumentException("Batch ID cannot be longer than 64 characters"); - } - return new BatchPublisher(this); - } - } - private BatchPublisher(BatchPublisher.Builder b) { - conn = b.conn; - requestTimeout = b.requestTimeout; batchId = b.batchId; + conn = b.conn; + ackTimeout = b.ackTimeout; + ackFirst = b.ackFirst; + ackEvery = b.ackEvery; + messageTtl = b.messageTtl; + headers = new Headers(); - headers.put(NatsJetStreamConstants.NATS_BATCH_ID_HDR, batchId); - lastUserHeaderKeys = new ArrayList<>(); lastSeq = 0; state = State.Open; } - @NonNull - public Duration getRequestTimeout() { - return requestTimeout; - } - @Nullable public String getBatchId() { return batchId; } + @NonNull + public Duration getAckTimeout() { + return ackTimeout; + } + + public boolean ackFirst() { + return ackFirst; + } + + public int getAckEvery() { + return ackEvery; + } + + /** + * Gets the message ttl string. Might be null. Might be "never". + * 10 seconds would be "10s" for the server + * @return the message ttl string + */ + public String getMessageTtl() { + return messageTtl == null ? null : messageTtl.getTtlString(); + } + public int size() { return lastSeq; } @@ -129,42 +99,87 @@ public boolean isClosed() { } public void add(String subject, byte[] data) throws BatchPublishException { - add(subject, null, data); + add(subject, null, data, null); + } + + public void add(String subject, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + add(subject, null, data, opts); } public void add(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + add(subject, userHeaders, data, null); + } + + public void add(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { if (state != State.Open) { - throw new IllegalStateException("Batch not open: " + state); + throw new BatchPublishException("Batch not open: " + state); } - if (lastSeq == 0) { // first publish - request(subject, userHeaders, data, false); + if ( (++lastSeq == 1 && ackFirst) // first publish + || (ackEvery > 0 && lastSeq % ackEvery == 0)) // or every publish + { + _addAcked(subject, userHeaders, data, opts); } else { - updateHeaders(userHeaders, false); + updateHeaders(false, userHeaders, opts); conn.publish(subject, headers, data); } } - public void addWithConfirm(String subject, byte[] data) throws BatchPublishException { - addWithConfirm(subject, null, data); + public void addAcked(String subject, byte[] data) throws BatchPublishException { + addAcked(subject, null, data, null); } - public void addWithConfirm(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + public void addAcked(String subject, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + addAcked(subject, null, data, opts); + } + + public void addAcked(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + addAcked(subject, userHeaders, data, null); + } + + public void addAcked(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { if (state != State.Open) { - throw new IllegalStateException("Batch not open: " + state); + throw new BatchPublishException("Batch not open: " + state); + } + ++lastSeq; + _addAcked(subject, userHeaders, data, opts); + } + + private void _addAcked(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + Message m = request(subject, userHeaders, data, false, opts); + if (m.getData().length != 0) { + throw new BatchPublishException("Invalid ack returned from add with confirm"); } - request(subject, userHeaders, data, false); } public PublishAck commit(String subject, byte[] data) throws BatchPublishException { - return commit(subject, null, data); + return commit(subject, null, data, null); + } + + public PublishAck commit(String subject, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + return commit(subject, null, data, opts); } public PublishAck commit(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + return commit(subject, userHeaders, data, null); + } + + public PublishAck commit(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + if (state != State.Open) { + throw new BatchPublishException("Batch not open: " + state); + } try { - return new PublishAck(request(subject, userHeaders, data, true)); + ++lastSeq; + Message m = request(subject, userHeaders, data, true, opts); + return new PublishAck(m); + } + catch (IOException e) { + // done this way because PublishAck makes an IOException if the ack is invalid. + // it was done that way because of api backward compatibility + // just no need of the extra layer + throw new BatchPublishException(e.getMessage()); } - catch (JetStreamApiException | IOException e) { + catch (JetStreamApiException e) { throw new BatchPublishException(e); } finally { @@ -172,14 +187,11 @@ public PublishAck commit(String subject, Headers userHeaders, byte[] data) throw } } - private Message request(String subject, Headers userHeaders, byte[] data, boolean commit) throws BatchPublishException { - if (headers == null) { - throw new IllegalStateException("Batch not opened"); - } + private Message request(String subject, Headers userHeaders, byte[] data, boolean commit, BatchPublishOptions opts) throws BatchPublishException { try { - updateHeaders(userHeaders, commit); - CompletableFuture f = conn.requestWithTimeout(subject, headers, data, requestTimeout); - return f.get(requestTimeout.toNanos(), TimeUnit.NANOSECONDS); + updateHeaders(commit, userHeaders, opts); + CompletableFuture f = conn.requestWithTimeout(subject, headers, data, ackTimeout); + return f.get(ackTimeout.toNanos(), TimeUnit.NANOSECONDS); } catch (ExecutionException | TimeoutException e) { throw new BatchPublishException(e); @@ -190,21 +202,179 @@ private Message request(String subject, Headers userHeaders, byte[] data, boolea } } - private void updateHeaders(Headers userHeaders, boolean commit) { - if (!lastUserHeaderKeys.isEmpty()) { - headers.remove(lastUserHeaderKeys); - lastUserHeaderKeys.clear(); + private void updateHeaders(boolean commit, Headers userHeaders, BatchPublishOptions bpOpts) { + headers.clear(); + headers.put(NATS_BATCH_ID_HDR, batchId); + headers.put(NatsJetStreamConstants.NATS_BATCH_SEQUENCE_HDR, Integer.toString(lastSeq)); + + if (commit) { + headers.put(NatsJetStreamConstants.NATS_BATCH_COMMIT_HDR, "1"); } + if (userHeaders != null && !userHeaders.isEmpty()) { Set keys = userHeaders.keySet(); for (String key : keys) { - headers.add(key, userHeaders.get(key)); - lastUserHeaderKeys.add(key); + headers.put(key, userHeaders.get(key)); } } - headers.put(NatsJetStreamConstants.NATS_BATCH_SEQUENCE_HDR, Integer.toString(++lastSeq)); - if (commit) { - headers.put(NatsJetStreamConstants.NATS_BATCH_COMMIT_HDR, "1"); + + if (bpOpts != null) { + long value = bpOpts.getExpectedLastSequence(); + if (value > -1) { + headers.put(EXPECTED_LAST_SEQ_HDR, Long.toString(value)); + } + value = bpOpts.getExpectedLastSubjectSequence(); + if (value > -1) { + headers.put(EXPECTED_LAST_SUB_SEQ_HDR, Long.toString(value)); + } + String temp = bpOpts.getExpectedLastSubjectSequenceSubject(); + if (temp != null) { + headers.put(EXPECTED_LAST_SUB_SEQ_SUB_HDR, temp); + } + temp = bpOpts.getExpectedStream(); + if (temp != null) { + headers.put(EXPECTED_STREAM_HDR, temp); + } + + // message ttl can come from the BatchPublishOptions first + // then can come from the BatchPublisher second + temp = bpOpts.getMessageTtl(); + if (temp == null) { + temp = messageTtl == null ? null : messageTtl.getTtlString(); + } + if (temp != null) { + headers.put(MSG_TTL_HDR, temp); + } + } + } + + /** + * Get an instance of the builder, same as new BatchPublisher.Builder(); + * @return The Builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * The builder class for the BatchPublisher + */ + public static class Builder { + private Connection conn; + private Duration ackTimeout; + private String batchId; + private boolean ackFirst = true; + private int ackEvery; + private MessageTtl messageTtl; + + public Builder connection(Connection conn) { + this.conn = conn; + return this; + } + + public Builder batchId(String batchId) { + this.batchId = batchId; + return this; + } + + /** + * Sets the timeout to wait for the acknowledgement for acks when adding or the commit. + * @param ackTimeout the ack timeout. + * @return The Builder + */ + public Builder ackTimeout(Duration ackTimeout) { + this.ackTimeout = validateDurationNotRequiredGtOrEqZero(ackTimeout, DEFAULT_TIMEOUT); + return this; + } + + /** + * Sets the timeout im milliseconds to wait for the acknowledgement for acks when adding or the commit. + * @param ackTimeoutMillis the ack timeout. + * @return The Builder + */ + public Builder ackTimeout(long ackTimeoutMillis) { + this.ackTimeout = ackTimeoutMillis < 1 ? DEFAULT_TIMEOUT : Duration.ofMillis(ackTimeoutMillis); + return this; + } + + /** + * Whether to ack the first message. Defaults to true + * @param ackFirst the flag + * @return The Builder + */ + public Builder ackFirst(boolean ackFirst) { + this.ackFirst = ackFirst; + return this; + } + + /** + * The interval to ack when adding a message, after the first message. Defaults to 0 (never). + * @param ackEvery the ack every value + * @return The Builder + */ + public Builder ackEvery(int ackEvery) { + this.ackEvery = ackEvery < 1 ? 0 : ackEvery; + return this; + } + + /** + * Sets the TTL for this specific message to be published. + * Less than 1 has the effect of clearing the message ttl + * @param msgTtlSeconds the ttl in seconds + * @return The Builder + */ + public Builder messageTtlSeconds(int msgTtlSeconds) { + this.messageTtl = msgTtlSeconds < 1 ? null : MessageTtl.seconds(msgTtlSeconds); + return this; + } + + /** + * Sets the TTL for this specific message to be published. Use at your own risk. + * The current specification can be found here @see JetStream Per-Message TTL + * Null or empty has the effect of clearing the message ttl + * @param msgTtlCustom the custom ttl string + * @return The Builder + */ + public Builder messageTtlCustom(String msgTtlCustom) { + this.messageTtl = nullOrEmpty(msgTtlCustom) ? null : MessageTtl.custom(msgTtlCustom); + return this; + } + + /** + * Sets the TTL for this specific message to be published and never be expired + * @return The Builder + */ + public Builder messageTtlNever() { + this.messageTtl = MessageTtl.never(); + return this; + } + + /** + * Sets the TTL for this specific message to be published + * @param messageTtl the message ttl instance + * @return The Builder + */ + public Builder messageTtl(MessageTtl messageTtl) { + this.messageTtl = messageTtl; + return this; + } + + public BatchPublisher build() { + validateNotNull(conn, "Connection required,"); + if (!conn.getServerInfo().isNewerVersionThan("2.11.99")) { + throw new IllegalArgumentException("Batch direct get not available until server version 2.11.0."); + } + if (ackTimeout == null) { + ackTimeout = conn.getOptions().getConnectionTimeout(); + } + batchId = emptyAsNull(batchId); + if (batchId == null) { + batchId = new NUID().next(); + } + else if (batchId.length() > 64){ + throw new IllegalArgumentException("Batch ID cannot be longer than 64 characters"); + } + return new BatchPublisher(this); } } } From 3b102fb394aa7691ea835c0d86639c0e8bc9ea4d Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 25 Sep 2025 16:22:08 -0400 Subject: [PATCH 044/135] Readme --- README.md | 2 +- orbit_sm.png | Bin 0 -> 1004998 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 orbit_sm.png diff --git a/README.md b/README.md index 77d0de6..888c663 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Orbit + Orbit

Orbit.java is a set of independent utilities or extensions around the [JNATS](https://github.com/nats-io/nats.java) ecosystem that aims to diff --git a/orbit_sm.png b/orbit_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..45c43b42603005a127559b7d4566cf1bc093d206 GIT binary patch literal 1004998 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μ$4}V|lqUt4vVbLI!$puP`GD2= zR`)`8dj*V9T?-gM>f9-J?J9mCO&=wwD+f~x*v{-z2}vj{J)rkvQURElbGNT6A)*zmt7m*A?t^8H8Wuh z$trUHYzZ?8R_Bnw$2A||FUOJdZMiL*xkcNiQulc|#3ll-OlBCS${@|ShwmEQXJBK- zCX8|BI;WHLPxh_<{QeQzF$w)XcjJ7yaiVSP{+ze22@HI86Efh~WVB8}B8uy{bAU4Jq z<*jS%A3OnJKyt7{8tX`<=|RJo{1TZ!O}Hlt|0*miA*|ACnP6u-9(}f6DP?sHZahbk6wBuWv_Le zu)Ysq_6hi+^21k4q#2mBkgSHU0yF26+tTGW8Igw(&r-#cZhS8{j3KmG|3-jlOGs|e zF+xcJ0S7cufFR9aU=~?}?5oH%>$(Q8o~(9_Z2|%!Mr5>K=$|SLj!9pWGHQY^J6^6} z+~u%0D*)L~76SydHQSucSaX4ojFy}MaFEio8K9hl(nxBw`D6)xCc!?t3d;C!a+%#! z72?g^F%H^S0Ye^6NGQ7@>0zo#k^*uIXs0&48bFMsn9Y8 zk6osuKVqSRiFv?0#XKLQ%7X#p=g%L^E@1L9Zac%)_Y^1<0U+JvhUX3Am8|5<;5u+G8W?V88u$V$46zn>ixO@m&6AF!bC2a&mfZ}Ysq_>zyc|9-|LU8 z<(&O!!D2p{p$7V8u758kSw2WD*s$Jdvhst0+#+kectSdo?YNsBje$ELiH@~@ZSQR{ zQ@^ir%(JiInWbxxoGW{D))!_Rz=D#_Up{Lrk$;=?_|^6SC#p6WvpSZR(!Oe$fpvUz zjEc&m`})EXA~Jtb+qe|^Z&n{lOP%MbRNwQsxc+5wi zY-sqaCREBez|Ymjt_Tv=aP{X&!FEk7km>&L=y6-sKt#G3$RiDd}?jV|Mtcs+HsG` z#CG(+i6a(SujU&6zQ_D+54l>~9F2e5F22`eCovoU=-|A4f6r&zezY|_TRZYIt$)OE z@7<$!*u2N0J7SV;4!(lFwryw+GT&l{7I2RDiU(mw*KFI_4)hNGIcN52;YLN1qR`0E6Jb=qQIPLh#5DwDn_%!RDeWSJ; zAffCBgfTjX8j5x)WYFU9eHi*a(u*y7eiY|(tPkqrh8EgkdOb*s%;$Km&AxLfUc+$& z!)OV6M2_)Qvu5A6encN9bJ&@ijSu(yuNOai``_30#62I}u;U!$%{c4jXa~tWJZLU1 z_l?hlTAX?So+{{)L;ENh>9%W<7Vg%M!g#e0oW|#oHnv6oigR=?nUt*V*6uuDsB;bT zmG!kpht!<$oFE^#G_3e`OnT1?(Kp(|zeLs~L4us`_8e-5u_iS*yu&)d0D?9EF274g z55c9J9c=U(j6*!s&YbGn_l4uoCWGX-o>@E_hgv7L^_8AFf`PRGV2x`&57GdD&ZxsQ zMmat7+uIJt^Ag7v*IsV1zHMa@TD@27n{jB~`f#07#-JgaL+nPNqxAzjLy+&H2DTg+ z6dX$BVt_6Vc{5~sZT#&3BPRmG_V-%fe5?njsKHjpA=J<;TbWT|5c`22S#dlpOZDKM zhf68+-Nzqe>$Fa`&DXfW55*SL;G9cMz|lE5$9af@f5vvkNUV@r!y7>SjfavP=ZvU? z7%#t%002fN0)#Q4H2R2I#+x;&7q8JqW6Lb*JUxVoTm9@#C5&+w>kpZiGe8_32-#`8 z`Qd{4;3olG+{R#K;gB-Lc*6Ln_cKZB*oL?c_*`Ni=UV%h@!>9x5v0|F{|ua`map)` zE&W-1jxnZYFnG27+^-OvmcvLnfV|x*JKE#-3EnYbLl%(9wYa zVe?+D0HUVNI2l-Iv&`!3;yH7Hoabn^*TYZDKQiyN1}2ges>f{O27&yAIW5KFa}jWa z`*Doh2oNRl;7~yYuv~CRDsK;9E~{Mt{D|Ey!YV#ZRqZMIvD9!4A+!2x1^35bZynQl zaew7r!q`tx-Y^$7{xf3BU=z>|OSxCXnCMamu+bH40+UzQIAL2|u6l@U;KmFFGyopR z5s%BD2fS8CF?lYCCIblL6q3~o5^#o4-`d!zRl&mmgJNpw+*>#CpL?YN$|H(x0^pFk zoN6A{yOp*wa+3j93_uo#bw$RJXRG$R))Iv5=};z5e9{Bx$pAOqqX4|{HAC3R7zf%^ z`#NT4kY0W~>EEVz4;q(mtL?M~?P%;`>CiaYdQt%5saz)Nw3g)tdls+ZX*F0$Evic;TKY@r~A*mD}d z{Jjy!4r?9cXA>E_tFQlYU7vMktIxf@ccbjI%FgCmOE!(#Ci>GR{-l(mM7IF8!g9U- zc}-WT>53Yp9&a_aOZ>D1EnNff{6MMT1#v^4F>W`mvX%^_lFI;Y#m__m;QM!NkJjnV zETDsHpoZ!KCSH9ekiGHM*m_sgZXS#sEA_<(D2Qav`D~!K04}T{efjf;vZIAvC%=pT zN?%W(xKwTgTj#8iZAsv2GTvbN-U1504s8P9HylIX~$5C_u#I zl9RD6b#SeZ>-A(0B5_FAs}k2$)>v%)U{c-*Y-RCRt+mcM2pf1D z@*Kw{-bk>Z|^l&R;$mNNf*i=_YkvWxk2Zgs_u^ z

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

Q@BGg15D_3Ztr=r zc@XcIfZgS1Tvd6g(wE|-ZgcHeIu3qc@b`OlFi^|Q&&Bz*Jo~^rYBm#F)=y>O`LTbp zwS$(+`~~vW{`ddjlkV4k^;aK*zh9^0nYX{){h$B%A2UzzQ`Y9GQ!fR1`pFg|sTAFu z1Z2cHAWXNJox}J3P8bxr5pcz~B&y|SI80TIfQA-QorZ-@|mjLrR{C! zn%neK`#J9;K*h_I3E}ObNgo=MJGa>qh~p+HC+XB@Of1SUSYT2X zAQb}=>lmc+N?dN99-mB7@yWO~=9YDsk7HZgR24k-#(exSYms&(>(SVCk&#_#;6jLP z?2^l3M?3WcRnq@SK3M|(gGo`_+1mLkXKia2r+#&B@u%m?STXynBB&ta$q9IE*Qf$P zWQ_5?%Ca>Dm&iJI(FphQaME^ktnRIavUlBBe{HwM@_MQSFD?a;X|nB_Yu(ZwL|>}G zvX%#z_Fii+mNK#>|74}96Ext8V^MRqE08Vq@RM(jfwyj-X&lnP&?KE11C#n`U0y2y zZ+)Zc09GGS{iyfUG!*gQx4pQ2&^EAV2JHi5P!1OGXnrpBIjJpHlTDjh#b8SbB5+2; zsQl#jEI`L4V$tm>r9RrKFhcw5K0tPEG)$r8ViFWsU|qHMAKEA4s#+sNZ3SQ&(9>}x$&iN}Kl zPE2DhxZ0)u!`>ClZfw4a>(5wBD`%D1k(A5kiOKij_cR96@w%rdbt|&^u3kp|%;OpvmMPu?+`fajPv)Jv78|DRbnI z*sU#Sg3AW7^KtDaz-VOwqB^c(`TZ39x<6#6cPFO|XrKJ<CLbkW1DIgRq5dIY1v# z$9XWmx#%9o-V>`j2}IYRJO$bJ7OSyMrcSW6zqFI2NvKTr+w6P@(e{Efl9L2#*xw8Q z%-M43piV!}U;1(eYe@q0Q0~P1oY)C^?^Ncu1cv4JOE<^zjdWvvr4e|g&9wY^bAV@B z@069CgNkz?Qi*{)cW!Q^d7Guz=WUzk-YvC%%H*`)_5+qi36vOewnA3MuVzE81YYDU z^Fshp-e)DdDzODqwrsxP(rKQdl(~`HN{Kl)2mY4WfTe!Vfu<-)G)xk3DdVPe`VeK$yFKO8h0h_sv518!O3y*&@<94uFJ=8|p6Ap@;z@m&)*79% z8kb;sc@boi7a132LZ`OcSKx86875zc`=BpS$9#@R*~zx7z|zUSr5OY7SYB<*@J5-|*BM+~5A0pJA@w$H&WaeBdX4!oBMUzMuJlKk=#G zaBbhkjR|fHR7!${{Wh|hBGSlae`-JKBl@`EATRHn|ChpQp!6Q>ce|r>C&!F3_-%hb zvLv00b3|H}In=>S3(UnA+~W4BY)YzcXJ09ZVtyF@6o5~* z3g^$zi)}DJruu){68dLz$&EmQ3tl$bIevzq&mTlKRsFYDK#Ci0Kc^Kus6wqpU_YOS}GdI@4!bT2K9t+*X_Ko1y%)6|9#PZpyqQ@)gxP5ZM ze5=mX7mz>tVjK6C+jC(Tk+B(uh{p%o?Ur`NF*^E=`pH|Py=-0k(tY*;n82$ipWuzQ zUw)A`zPxoSZfLh-pTJM7U-K&Z6sU$8I0(sa%Htm&PxJl~`I(Phf4*_M@qXejM*F{W zEBZR8Okxi@-3$dr)<8x^*>beh+6yy^HorEkx7!afCikt8s)*TL-`oQp*D__Kc19ZHP14`gnZCog>$d^<<80Cu0+G-&GJ9RHzATUVPCVz2?=FgsxAIT-7ebpN#hg^R@0Z zXZhGB7;x*?zJ`G74tZbIbp=Q!w|?w@VPJz{?2b;xep=CQc;@Ne5Nzu&HqJnXp&t99 z7HRD9WVgHb6=Bzs)j7x-!I;ObutF=fL#k)syz$H3!+Vs9Uf+E+*XK2Iu68^6Q_D$j z${&!-<-5*u9c_VqJo>EN17Y`~%xHt|Qu>}!x~sJSY`32jS!S?<+s)|1&g7i~HU?R3 zGSGK2clg1rZeiTrbz3lZ;eVfa9rFtBH19IyRaHj0GCAeZuAQ>s!LEf5Y#)4?ZB~Op z8w9XwmbbAW!DJ*sH(-Fncp3B{t1Y$`v6xo!>^NTsR?H|&AEthx5pahFmBzRl5 zT%789qNn_FbNniO_iV`y#k(h@qwyJJg&{S3w7f-DY&&c;2dxudqQEmGlB@bQ8Kb-Q zoD$>AQx3n1wt<{+dqMUmB!dZHSBwInG&bsrvQ_N2O7rB(VSv2_LJ&WNZxUG#k#HwK z*^m({=S!Yrk*t=n(b$-|LW$rI2a2 z!MSaFU!N`OsloSTP$Q@9YH^!PUgstjv7^=KI~K|@T+&947Pr+W+CG5DBTxId1Ir9~ z(yPdX31n9zuEO?lw23k*=sP( zA_l*RJT~4RWG@AbXI;rU3)}&VSk@ga+u>sOLIjs1c&$Mw+uZ{hKchXuF2EArfcdO_ z<8<7C`3vSSb5W1?T-2f*)OHuTPSijkFF-YN#K*)7bDM>wjrN-W82M)T+%x9D;YZ!P zyy^LKpzfjQ{(RHzy64XU5&6b^3OF2Un?r#7p}r`Usl<9M0eRPf57)JM4r*Li#{6|{;x$jE}DFEV7`_I|rP3v-9``@%}=Gr~&m+S2FkiWXlKjdxn^wUqfk9_1KWHmlMzBWg0 z_fvlv;HID3Clftp_C9vx!9aUCDIXtSljEsxd5UA^eeZjp`_F#*r`*i2kQ2|_x_~&k$1nFdD$NyFYf`?^)G(-!|r`Q_G9j^ zzT+A99P)!xmfN{W!gjKG8w%{@F_=yg+Sh)lkCh7E;aLoXex ziI1ozyVo6z2&~4ks9fBHf~b$MV2Et|UQMkbpe;YEd@Mdw7+5{@x3I$Y+3-_=twK}K zlS}4NV_>CYhdFuEw#(N;Lx?^`wrPS$Qirg2FXlkD_!kClO)kOOS@zN_NOo!)wnMFR za@-u(eKF_acRm;sVJa)Z8I=#W{xU|V7DhJoUkQho{--IW%rEO#mjw8xb#yM~wvKJB zW5Xn##c$TKxt;X0*@SaR;FS7rKlsVthHTF&u}K^N-MC>*u+8t?lOrUtN!id|CV^Lj z1`tTTM*>6&AQ%f=a-ymnu&vRjW{W{qCgqIB_iblRB_3jO7BGI<>`&R zW&j4jI+bN75RU*r0Qb0sVqwBO{Ol5vFW7f>#l+)9XBd@0Lx{^*-i1 zM$S^FOu_2-NEoA#kVf9!T7qbdsc&^+TMf!=hCMa{5|LpJ*5FPUtN;${V*v*nfWyd% zezb~jJM{Gka9AjSS*gUemY*Ow{~n(SS#>7?G$J`YP}bXP9%I(K8$_abwQw0nN=;0){#P`|!C&fknT?vKcE5_R7F2377$p&GjvE z^acqE0Ps*H0g0TqRRTQbKG!k>SQVe@DS1!X-9hFi-Qu$YgjVu=a(vX<0k8+y27 zb4suX3-r-yn3&M)QeFQ5KqiAVS-NO|tG#>iw}MOvSfJNn8N;;Rawg$hX&{;AF4Z~3 z+P0z;d#}t}xlg*bGv+EGN(4luYrkw_37Vh2?=Q73D`gZ8n&Lxpp3=v8X=8w`%(?8R z=P@rUf(CjCvXcH8>pWQgROB$-=(^X~&4GbO02+?y|Bdgx`X&WE zYmTj2@?`^L+dhaO8OlqWW}aV3)8n*W4A$|Dl-Kvpa=P;E*xY~<=BfdLwr*z11hZwA zHtRrOkMGN&O1QF4_i$u9qCA5JB8UwDlt630Sn!y!n$24-7?9EXt=J}|hb510d|#br zo9Rc_Rsa~?-ug`1t%J05=%YQ`euXekt7%`rnAdUnWXyXBDDZw7KTW-iOj*dLFO}g< z5MN8$#2ms#fG`HS1gl7FkfqMBGKqAw1<6YJFNJOFCkD2C*VESMP}ooM9?){UH-4dY zjH@~Jldy|BmZx&lT2%rddMT$f3F}E^1pgkGr+DgRuOPhb1$gs&UIBP7(%yl={I%L* zHcEY?V>d|$;U)jGy@Ofb;9d-9F~&hDmu(Iz6mgr%FfJfD6K=@-Z_&t2ZQpm4`1sl#SArgF<8ug+^_BMFpc}=u3Bx0l@8{gK ziMr=Q^NSuIUy}nt6aVe6{hIsO$3N!&uI2~M*GswIDCh1@0VC(41X^dzJ@XOb8@br0 zjGmJhf@GtXv8HBDy=)fqcYLOcj9RZ781&8NN1c=Wpm+h%?|j(z<9u1Er!_9^wHLXm zLFW@swKg^e zeQEugE5D>ZU(>urwwARSzFT5*KjxD6A}JX5S<e7wQ}G6 zUEk&Y_Rs!|`;Kq__Ltk-A0OEPAo!nt_A~CoKmYUY?~Q)o{=Ju6)LJ80U~+OH`u-`9 zL&GXU^4?dA^roaY#TT-HKOU4ut~sc?R>qLMyM>8e&*#`CuEjl-aT_VeG7usF6+v9}A?f<9Qs64);Kjzif?Qx;#&wVSFU!pvrGKL= zrEPj909_;q1UJe5?ysLwpV+o4w`hQ`t?l^y5TXwPql+@C2CR4={R({{HV91y(S?}9 zlhbuA%2{AODV;|^;ilGh=wzcCogiSaJd!o9HA;)L$p^5jU{M3q3sXG6_^We3|C z*RQX#o4+9s6dw#;C37?iMAF};5_aR4oid;Zz9`6mYX;4KhYjOFRl3B`YPJW#x}l8{ z>y=&CPcnPUqqW#)S1Vwla>fP-x@i8g(T8`Eb{rc=HCgy$$2r*u?_)l9mK)d9B0Wu( zq?2I^`vB-Gc`8?~-&~UAyF9rSZOY`l!NkHG>Fa5}!MH(NArEVZ{JpAdIqWuKTdPF^ z8p}M?Z@M^EkaM|j_1yv>lmZ~khmCQ(*^PBWp16hUhK6SA6+jT5qJfExw3SooVPRPg z?zt~~;pMihM?TIjFJvvEd##+4k&k;EO8|X0>OU6a_yZ`|2A6ID3)qGe(6;x_qI_^u zi^|FNh5I}YEOjz5+nq4?#{bao@YkK@Uf&C3p#<^htGBX&kG#KRX9HA%O)FCz&)dE5 zk~5Wv`WWobMsrDOeP9P*{%T%lXF25IFW@&$*8B|2aK=PbGS4svuS%Rtp6H%7Lck5O z#48DIXiio#4{Td;#mrrRTLEdj z?vkVY-FOdxq20yCoxJ{=2?8%)^@RB1qviPdC4cZY^UeaW9fGo>O*XsboV7fHjTDQ?Rz$YE8nCF-Cx%!Xo{rhZhfZfOg4&PDhJkVX9i#iDMa$A0S_$rl_ zMiKkI6<`_6;4~PslDxY%r6d2YN58i_$*ruuS#{oNV?%rH+OzwvzIB_7-uB{*z67jN z3q-P@D+Gc9b6T!X#xkFcWxU5+xixas(%8oGZ_f#yI4?FfblcH3))FKGAQ%37Y%2hh z0RS?}3-H|b*2nWve~cr5k;AGQ|9vsq<2mPHC}p*mXX5J?qfKl(-nT){SB$^Q`*NQM z!YKI4`iqUDEdYyyeLFI2N zg2Dna3j=l_V^(s}kNr5-bGO~aAVOKDAn)gn_o^1cZU>u@Iqb)4y3M(a-?g+K>7UL|8*S8l0oWX)e-vtF` zai8sI&y~uXBj+xjv*{#1^K$e@vc9jUB_XG-iutB_U1%ap8huX%0}UVmtK8aH@&PZT zeqdW`&`Mp&8a32jgTh9;N1%g~cLUS~Wdws9b`so11A8o(M*Rm{-vB=p0bknaFUgC}fl{UK($8~gwbBNDiS;$dpi4K$L+|Rc__eW&Nr2S<+*3f*>{uL!%@PrVJ3@ZT8c)!+O4=hgiN!+ss%-d-Z{eaxBVCsbhwiCOw7KWxl_YWJIR z*F_ApwAOGFun<`c5qVAm9`Kti6Y(K5+mOVklV#=z92xLRFuJPt#}PLYYb(C|?Nr_v z8xgjlYyowCn%`KuewfM_CT!POS6%}_T$sM>wQQy~z&wrmd~VeH{k0~`(rfCHx+Y6f z0%0^8ocbM(Q@!3)5wusdUN)}v8)F2koSm_psc7ti0xvdSdsPJt)Bd{V0=4>i@1t)4 zlf~M^avh>CDuRZVKkXGV*CR$~*Q);pXfov^L*N5_E2RDs&_BdE#*0L5-wE@FB%Ue; zBXHxq6?tZEqV>{#Xd`)lQc2dBtJlZ*tM5db7T^%%%OzDoqh@S&q|&k)Y*59q79wkR zvSvp%AT1_nfL|>E7!GpPVBuEY@FU6NIB0)D5^`xdD?^k9A2e?yz(2}kBFJKOG`(xo zmWrV>gusbuYCvzbh~v^yMv%FxAUV57*`jFA=4cfGAn^%a0MoXM64C>}#qqlbXAfU) z%X;MFxi9_UWMgQ9W`9kCF-F^H1pp%0t#f{8B=2od<|fY_rt}(Ut4gnzIKGN9uOi2< zfg6yHhK@3W3%V(1X>Ib}mS1p?MN81Nj{@)vvRP>#sl+B(>bOLMQK%x>s$@I>(O!W2 zO68sj#15K%pb=K$;B~E~tUU>E8_zvjPtyC9d^WVgGADx<;O$O>TL8S$XAP7fMxC40 zDvpot>_W!4pVoXLzw3EVz!#D$H&B|{q}A*7ia;Laf&um}gt3Z$51Sp?&H#`FF_K^i zVR8z)RG57t=T4A)5LREUIg3NG2DLz~?(8hex+CC((!5Iv00cPCtFX?+`Y@<%c$K7v zKSSV-bf~mtvmkSpvd`Ns%eTCJsuIhMv&CekDGLaqRm!ZcN1M?g5?t2<0Co!A)|E^1 zrAyr_x4d;n<}XMTTkcYnpZ1#ug$HF4tMX4H;4naQ_>_*IW-l_(gUC3eF0eI#s*uhDG7q4v^plro za=ztZ1ptm983afGI3Dl0Y;F_a6i|3Ay}}r~ zw7p)yU5=?Bc1Tb+xTrQFz^O8@bxuIN$X6VnC1dm?+7Q6Vn8P*%o$nOL8|M;&S(Gjw z`zokxGi(gZ%Ryw61$+~hB)r|AtYxrCJr3S08%w!__n2*&m=$e#oU|%g1XchVS znnP69z5w;A(8RTY)y<{rSFKm&rqmQz=Ruko_gvIR5io9hNku?G4lijaD03Vt)|YbI zXiqXl5x}(-6%>6(0N6;@H5mI&Sj*;jtN0#aJ5FG{;#F5c<6!S9qaSH*MNa`c#TpE$ zZ&0khmws%t%;g^wQWtE2EmLK`=(OnZPi$QYr2EVWIkAJc!&v1aqYp+i9whXPkhb+jCo`Spj! zdI^|4WKV|zqw@RYzWERXzf`W0zsTz{m)|)rOX|x<1mQa3pR%LK_{;{Z(i=2|LHm~wm$likGMCDW9#pJ_(Sfw=bpPV zr{KudIZEt&13H3j!Z{Z=K!WCg8&bxpUK9F#1%+S|uP&JfO`Ku!rRp42~W14OllBq-6?y?X&*!~VL3;Es;%DSF=Ni`!NIZvh~|HbSfE-3QFlU;A(V z{$tkfE9gL<{ZGI7o9^d+@fY2Pf8pmHdw!4&fWcV191o74#V`z0MH48(2>>kT7&||> zWb+ zJij;QZ>2#2*l+^yf#)N?W0-%x`bImr+1IJOEA}z4nWvk~W`{C|^^+8$0b&{`1m>*Q z{P1%A6bwbS)}Rc;K1BPocapl1jWioD7Nh}?55P2j0K|xluL2N**qE_pELaH}#!feo zE#D>kO!xat=|b!!Xpq2I{@qT158WUYFsk5;mJlH?FaQ;AeX6uoyy>B?yPyg4$L#o(LsPpF!>?9TM6qC zb+~u-z&-z^KXk8u?Ki#LCU)cF_x|7iM`HkB2X$n>a$^BTppnH~w_6WvuO1qyI$?T?BU+J6z|R4(wEb-Ak3y26+4Mg6A%uygh-yL{Y}) zpe=&r%J#}&Hl-Q>z_8_hJO|@w0g!!cKjyoIZSKZCSO|mH0OqKQq0guk0QMT>kF})N zeDQnpfZn4!KO@lq6&es@(F-$H<2Aizd?4mxbH3{_b8;R0rZXiQ!Ryg3EAZ+>#Sf2S;6Dq+k0Ok zt8|Gxwe5y^H|xli*G$BxiNIw%4BP?z51`}bT%I3;%K`yl z0F?kl8|Fw}Y1)UeeHLrU(K_@BK-N)7Wnd}+Pq3JI+Z8Md8n{Fsd4wFey)uzog3ry- zEw&2*V9Lwxq7P>OjrM_D)66YPmgqoGs_{o;9gFn);M(1VVB%;aUn`(1{>Sor;JLmY z@6)d|FYyrH8!0(#^B*0WwjWuDcD=B@x0?s!Si0q!)h&XD;-l6IGEGWLz2;{oi`h%= zXs(m`RzbH;K;=PIIK0Silc}qIm20M6*xdktVQ0v1Zu=Hs7ej^^OifuG^}=jzMtf>U zzu~r$!+EU7ZoCiHvN)Fjssf@x13#nDB2c+lp0Jz%8T~M5xm60BfyWC0 zWEHIBIPfy2Wo=eV007#pT9Kj89KX(3u0a4zm5dG=Y*GbkWVzmpEO0m0d%d{JT&|4* z;M?w@u1N<53=zB>?FJxaN0xL?mML@XR-WSvEZ*hfw)meOfaiMbGuT!u0d~4xty~@3 z#3rjI2(XGgy`29PbOrznAXem-8wFb>I10~Ljo(Y2TFF5RCNPDR0YEzC3Wr))@06(r zxZ5V+CFZhLf%S#~0tAo=0QUs1jn&Fr$^baM0HFqPcH)Ejn8eN2lutKKq`X(X_&V6L5{HfjH z$T#=%bLIkHQhDbzk70U_wRbu$O1?R5)AYxlTVkh`o{{&>oW19Ll=p$PNj^5`fal!K ztqgXb90*PQe@V)IE;ysq$GI(~<1Ziob9PknS?L%rfi3wQm$pYb@6846LDb zcI^3I1$Edw+Aho+=F;&}MNp=+eu;VU24g5{l6<(+iNT4<1a)k&U8 ztT%u7-S2kqfA4$UoyWO;Uy;XqfAmM)5B&AN?*7^{?{HuE(w8*IK>#p`xL1GB3IJAn z+5EOm2X0(;nnSKJf8ip_%sF`{=_9)#S@-t>vbw!-pkSjHAixwT;v8hm5nD$p@QR~R zc2wU@?=J*`g0STHe0)A=l)M&psCo_7&~;eXO>9{`CyotV58zEf;<(1~mRC|4M^&wC zmMpYUfz9lS@}!rtfjD+5`o3P)5y&$JJLSCrDuQ`PX;)?N;l66)w4cS2fdL#N zR0R$KX?p8~d)N<@prD7ZX$sZi&mwA{# zT!}3z&?3Zks$5j@N3yM!RE04Wu%skKKnMvJ6c~&ZAhJP1U~t7elN8}){Da5>mz`9E z8lgvb-@fO$_g;^gIleK*TyxK}*52o~K=)pJR@dos&)(}XA7hR=*Zju!4LcT3+=a~p zq>KJS=BLfsx!N5m%Sr;A`1xQp@@6h?$g*tWd`Un5o4-zfIcPA`*=ECc zJYI@6*yQmM6%4*E7HGHT#g_%tWT5qEN$$=ewJRBah@IfTjWD*l^@iGuHDs03HJ4wl zF&_9C#d0peN?218t@+;b(8nyHe11u7_~HKHak+TE=x++ra_Vi}0+o3t69+3VyjEPb9N=@r;xEA{ZO|x3R)a zHHU4A4@lSto8mEwAdb3Lx#f!HPdx>jZSKEJRVWvO$utvIc)wc7{^j}-^{YaekY73U z0RUSD#hPA&Oq#*QE0IEF=B}(0I+crF#Q7g=ZUiZK0DjHt4B#>YH(*z@l>FH}tV7z1 z6O^AQSv&Kxf!|+Pk{QYk7XV`!ICUza?llq`ewdlh^-O^9jW7lIsE=-m1T zmiYpCJsGU(B^ZWfh8r);)Iueg87vcUT|ipu9P$@4h}S9bQy&~l`dYykzK>dobOn5$1=dy?*KaU(0a99|CD$eVYq5MnfXVh!&UKQJxgc9xi0T#Jo55PF z8!?aQdmxsdC8Udsejdz6-b)vU~yH=DZYZT zZG^sLK(_#MyN+{P+~Z~7y<5xL);X&I%34>4VmqAxvg{)P$|@6?+i)?zgE7R?!Ym)n z^#vO^oO3X{BkkLZS_XeVFON{CO?Rq66?H5d+gztWuK>rgRzwhi+AYg8b6Ye3>_uj| z(EQA@_fTtyY$#iG8~wuIbrtHupsgs$T%eug8I?#kvoBz~r!EnpDdM_OWVUCz#~~>V z#Lhi{-KZLXz=K}d?Ba8p<}ymcI|ZjL-!RzHnmgDlal@F`lCDSC(iLoYNnY8|yGt1@6Y4N*?SNAeSYNNM0%g8-&26tb)Wf% zeN2H)`|ABlp7Egf%=;tno9nb?+CNj}n7YpxKst7hT`a=~v8i3>T*r3vf9Kci3lf-m z=7Sv9?W60|`SU45dgK7vE4k-bc^+ge=M1D*dR(Xfuk+civ9Rx1yNd*uB#B%+Gtz zd)`Ao^;17}1O7gwBUeKCe0{?%;NRH6H^;*}-tf#D=mX#VetO5--#!}n^or(A1%YDU z4s^Hpk`iSe^<9;QUwVy?m)fx}u!hG?1yNJxtmI%M!@=tuN|L@$J92i zoEhUOhVNZ|%}AYXBPV<4R-25rg@OeF$ZfYdrR_wLll}egeJ{QHTfg-N{C&(1W&{7? z&;D}&!DhoaEZUyj0O)!fE6KZ$_ff`duz3Y!bj-(bVs}gf*KGW#Z*YElJZ`-|=j(;! z#}!xCGd51I9O-kr?X~OZ`nlgOd(H;kV)ggi?Kj9W%m1z1!thFikhQ_AzIM+@dWfT6 zf$*WcV}7q!X5NgQAp{UlP^T307vdOxNh@dzJc zcM`;t=2q@C$iS(ZLbL&*$e@pbOsTDz{BGYz`PQNRRPt5t8Eq#1Pe0S=c>_TsE7C>( zJoNDZ$a@XAu;7OHYz{sLmY>xt8#tM_eYDr@dEaU9f_)}q(GT_< z!F6pvl^O0^0m)6Om}P1iEEh$QeyB4ExGlhX5*%V}K(ytT7{K_sU;nj-ZC2MlPA<;r zH~-!5#P-`>i}x`=#>?)ivhh=2M#@qYw&0)?Vm?HI1f-iaFKe2w`cXc-9~kZC{}qhW z-RfaoX(|tm2H%w$l%h5leRNqyhAIMYYGJaH2H-&ES;qiuaxsf6T|5C2%)F`YR1R9N zkLB&-^9y?pf_zAFpLSkZ_8tJDjD;2f8y3W5V@Ful>Yq56LKOkL1yxl7{-WFr`Zmq5 zZq>3OSg6rD!G8L|SCQtGw}2B*kpoh-45ei?TVSd5}4Et?Of0Bqjwp; zGX(<;V)4m4h^ZtfDv_|vC1{DVdDEFcRXgaF{R#Qw^0C{^;f8|E{XcMf*NAh6{bO??JGn_1Ak=E=2STubEO<7Yb4Mew)-lNbzB z?E`t&D~5m`M+!FDy0Isdtu%03;Gw-C;U_UlcpKlT}^ zEfty{7H9kIxgurB$FOa+xkA~k@JD^CYw28JvI9Uqv{AC|i8j`wA0!8LHvo`@J$Gm$ zWV`li_ayS%!kQl1!OCrN;Z`O{uCvrL2aH&4>{)yd+gq^;)KXI94wZO?g@xtwGH6Uq1(#*d3_;eDUjkJL+K!(*6tJP#7`EKlCI zdQV}3tFudut3_dN|_}23trABwa-&_ zRjcwuV{Dd`xikgp+V#5tiK?oud}q=AV=~EhOa3T(s86G$v+|B@Hsn5}Dv1g9iUp~fmotNG#x9wDd+BR0K9rHS-bxO-UR)(}4 zrsTeJ+s?tf+<&GF&V4>6En_~$5c#I|n=;UI+ZY?aDH}ESy}ADwW3uj=3-Ym=v-`(D zu3fgBjx#IY7)#jvU%r_*)^5Al0m9HnyvA5~RdvGv^lr}mip zo3#IT1>fZ5v*$na%rjv9zVVH3q?_a69NaH#2PsRCkDMi+{=^9P-e0+w9hT-dwGoq` zg>Btnzz_X+`*Uxnul&!ylK$G?`0MnEPkdr)ePind=_@Bv$JzXso+~niRoqCn+(111RbtBmeE3NTs zDSs=Q`V2&{^jtL?*7)_|L2N(%tl6wFZdppNaK;?*TY#M~y=9|jy>5oJF5jP-fW7pi zcTzT%+-1O`Qg&Oj*}%`0$OkFy)^Z;)OEH|oz(vcfMP`n74(Ic|XIj=r{}7o}>|ycV z^>V4Q&I)cgWmhslV}J}ci@h>IdQAsl>v-GP(oIC<@l$!u<0Fx|?F7W^*3yo|lAg@K zVFDaW__Ez`n}>d_mzCtY4A(I87yW;EAwX9pfE4GcEHr4NJP@Eoz=L3at4pxsxIbC? z6*hT(N%dku2TwmG%o77(LwRN_rR-$Wnr*ISxn9ko30YTOH_jKjDVdDTmFAGFlXv7+ za{;pL%xaMt#aL=!-?O{NgRfATXMkZbkG9B@3aD8Cl*8~@&NaVsBm4F&=gaoBiL;Kx3gH5vRadr0y04_6T;ggv;&)2#h z`F#EPAcf7$Ud3Z~K zBw+_b$`~y9&^J%NMr5Z?W?(3n%R^e-`%Li5Cp5qNbU1ewe!mKB(}a47j0!Wy4{^WC zlNU57Hux~j>l+M=EKm+PkdL1bmT(j7F7!-%6#7WGo{6VHW}i1!EpJpzoIyesFNogF-Z-=4?c=3KyKm4#hj+ye+$BL}OjEf)t; zxAp3trr?;XIt8a05Wuc$ZT;dUFLJnlyE&uH`lZlL$6#sB%R_@wB?zK#(1r}&b_(im zI}np#wv;{!+EXiS3`t~N8-@O58SGhQtW+;++k&6_1nRb0-xI*lg>e?@&g;om8I2bg zpT+r$gQqmOf*{2$GW|1=N$xHH0&-s2O>;))s}~{|iT7n*_f_0>HLhtwzX1p>1jJpI zPk~X{Xzo=$4iI%bD6)Jg)*<2`=K5ys{Im-;|=mG%lKt5yDko>FMADw&=dq> zF~Mg#EZ+Zb@XtFQL8(XQ+AHk@jc zec$b?+rE0{pwq7M@3O~TcD%1YrmoxPJErQjtNgp3yUzx$Q}=y-Zdbi__3hN(W9_l8 zpZ2wT-u^kDJXQa^-FLCUuEh)+sdo-Ir&99Q9sj$I>z*^!e`Dk556cd&^Vu%no1bIn zG3d*z-y87v;T~J#WNb{uU_ZCh-ml{$)92kBkH+yuU-U)vlOOp>{7yl{)CW)b*sXu3 z1Yc*NrxXDZa>I|;6AP#Lpyfu9F-H&Je@3Orv*QX=H@!!UhK>T%cJr<4QwdcAvSU5z z>O6L(3~4)NOyd+>%G-9#pQlmou^hUQw^jBSBRNKD5W*s1nso9BW`)4c)vFOj~rjqIWv*tyC6?-=-vId8Xoob}DnytrpkL^kQ;a+p1nfgzN&2WhZ@jtL8>G5d!Ue`K!={b6?R zEad=ib~noMFzH2O&nQUDK&HuYMz&lGa2DpCFak>f{o`0HVs24m8QXW*+||u{e4_IH z#x5@s+Xe{RXXA}zQ(6Lwh8*|8Anu}Fq|D}M$4={J?0ZM#(?E0^Ll*d~%wNf^N%5J+ z2qv4;`)I)mM(ceXMV9i(#RdJ)Pk-cLo7c6D_y6QSQO2OK!mX_=A9E4esY#G^RYY)o zNDHAsoyI_|k{qR#NCO*7n0o(8i~9vXgk3JI#@hmba2LfYW(p!i zZlpr*qjs!pQ~LW%O&r2r8ZTn^sg{@OU;Med&&FjbbJc~Q~IAl>>fFT zHN8cwW4XOZFW6jBPngmNmmT&5Zw8;xq8ex#s z`lLvhvm%MC3{GWi7YWOh0W`}8Jg_>AO&4Bm1pk z(%P=}j9x)ho6~!hTUHlH`l2h6!uV8K%cRU$dq%XNv>aPHCz0_U`;4{ z7sph(S6#+?#XSL+)?eSVeXI8w*uLlwB)L(Q8@&o5B~k3>;oaSWOLZJM zQP1f$Uvb=TPT1x#U^D^LB`&Iu?U6KH9HRqEyupvzjHoYowJ}{=S?lzJ1bOtS8VnGh z5?=^u>z=g5N|w4X*ekxNllKF!4Epe0rTK#$U}W0AS-1;CN?CgOMtPdUFlWx?EQdT# z<~}xZ%3RBzyDP^hKZm1)jm?lgKi`bo1-M!{QU-F)U`t>A();A>%)Afs@6ueLIm>U1 zl{;1U9OxOVXWB<&3|f2sSl#kI8r#skE{L-WjL$brbAUg$znnQa*2Zc5Qzl7TmYgk{ z_M?>}Wmit+r`;8RkTL-C^V098Y$|1d;v@ zf6nZ%RF*WgoAjPz_MfuE#(YWIXZz-wG*5PFw|U==fvNd3r`l&M@B5f<%&#BwKc}joWw1W^q1I zeuxX>t7AFN*CTBI>x{RX;|q7Z;hAUXfB&Ulrf>SY-$dX413$2}UJ*qABIE`d3HaEW z?@U9=QDm%^JnwVv9*0XAJT{RTF7;=oS&Ax%OOJ6~6{MkQ8Z+iC4^odVCFXOqgO*KA4$C5^~+qhyzF!qIzh>zmf9$1iUtICS! zwH$9svuzmzFgYTK+jQb9dfP~s&)McJ_ilVgvNf4^TkjR}{iOgEMNYNbFqSP3kl5{b?H})N>u9RvH48SdFQn#y6KON+Fmw`N-#-Gy`u+% z*?fZ29CZVD-GhzFzyp_^a|82cd)N0vUs;}81q>q44>od5k%jy?918S$m3r=Ny@Py` z!eFbJamPMab6|2#c&Elu0&od)-Gyz`Yb$d9v-BtTe<%0ZoSp;NSl+%ZdF2z?&#sfU zoZmW*YiqWbcC!Y1m!IEW3iI1lwdQ#om@EwZ@WVUKQ_7X9nN`Q^a?J-z0&2jvnfcA* zAl#1`jBK;*rATVFYXM(rCLkKmfXL(P3NVQIV4#TsCFJQXHCMRKtzZX}YU;G#J`!9S>+Lf8}ylET8AM(sD=fVb^N&my&Z8`xC z4`#TAZIE-;GB62Mf=*bg%tS4jEgbwtc(JA`XIwXrhXgciJ=CK)727!Y4_+U}+XXc* zy$psdw@slA2Wup@xz$>7b#q?o^%?TtmbZ`4rsb^z>bNb#=jFj1?PPN7>(AZ`<6}IASXw1d-Jz<#5-R~HMFln%BAscVNe0juu(6pYRK@JQ@1l!Mu}&QvDCdCmF# zQ1^>q^QU0LGPAi^U&tEe^dzbS@Vb>5c~PYY;GNR|0b$v4{$mENSw(@tj_&kDlNr@5Rh5RtIbPbxEIx&HlZyrU-7(B*2;QaM6eh$ z!RAtd@(wId$2mAxF9R5z9p2S|GFi0YodNC0QnR;VuY6x_qYYT13W|CGGppHwf}08` zX+3Z|W7qOZ3psa%2?@xIfnUDIM&_1!eh0uh=QQSW2`D9Nk=fDh2%?n$M41Io$Xi-1 zjsQqqoPP>Ak*oSBN?&t6XfFHZ>8Ij)RPv(=K#s{k!LQCM7|QJm(3|^Ca-VKCFYB79 zR-mm{WdUAC9mTZ{-?I+wQJ4e{AF!hXJY&;Z`&BA8Tq>YOk#V`+oXS{7pxpxJOFq>e z^1L0u{!8RiEx@F9xX0z@9@vu1G?w?t;8XewA!&2JOS<4te5WA1tPyHvAQwGy!y1@u zZ5^>;;qu*xHFQ05gyoBrNA-fp{}OhKG{xme)v&m5b=2v-gKaBYp7UGtXUzP?J&HL=PU}Kgx#6Dk@&IFEZFzo~^$wPBk9wv7B5cD9 z>UOS)a?bD_IHwerY$Nvx6A`W2Gx0z5A=JV6`gQn>a~zATwUa(ZZtEIt5t}Xku#j{t>ACtFC_Od@!D*uwv0xPz&?O}yoPap^hSdu zmXdQi+?!dyO=vS_KbO@Jn7({5=a=T^HngF6xc6dqC(6%PW8lRUxUdVPNss)z95_nv zd!2iZoilY$`+KZDDX^IXxceAi`FD2YGPU-57l1ql_KpFX`8i|Pr+`sj_8i>XRgPWE zy!`o70LRpQ#=y+6w$4H8REjl!{*>M1?J?DlV<6C2InvMavgY}YEIj+Ny)$JD*XeCAjn$`nwU|9i^U>H{%*QXuSPR};v~ALCiJOn?5I2lp zFPA5*u7PEVyhWX-cRcsD@blcmF~pnW3w|)e`@{e1kI`R!_RaLk=by)CBA1s2vC_cO z<=+~@t$P^)5L3b~N!Acp$AaBBjIS6#T)GIr4Y?Jsu5EobFAcf>WCYmUL-~4X_+xl~ z^CIPGx3kR*W9mi;dJZ52yT>+(X<$s1nk&}o>J<>=JN3Ge6d4ne7qBRjv?P?WP?(ih zMAl`eOf}wYhA-vM`7>o%ar^YGv3v-iD;S+#B=kwyoW12G>@~Mo;iAvBJgh~Ty!WK* zZzqgnfKBVQGP6{ooD#rczDEIJc1ROT6&Wfrhl zB%@Ql)AhVnLHppf_4hI91XBWy`b0X!;fh`P{vah>b3D{T^YL6`fA;5-3 zyQ$BzwbMG(KeUnL+VzmrW+_rWSoRq~f#(;pp^x#`L-N_F{{~2_8l1(!g52?ef}I8Y zrMEyl4fNv!KKXJZV?zTdI^^08&%V5rc`)=ZgU;;JO8}^0{^=y83i`aRVsMeOobpD_ zP+!xYU80QTa!GDpOFn2WKVP$$$M%#U3CRu2W0iqseg=G+&V>k=;s6qsC2!U`@43jv zl|2oXpD*MZP94CYE;DS;ItdavKUaUG;B5F~c63+Qr8<{d9_G+~47egFs%cOz2087y z5~*bVRNqe+q^@QF5-s1Rsfl=%uIK!>0CPZ$zhJbYAw%0huDMhY1`V%r&H`Q{XDxCC z3vgb-=T_1N2=3Vo>*h{?O``R%{&vE6v^k(_SDf1d>{lU~Sqd)A&x}COVo)_9iggqQ zruyxf1lIs8R%SPRDW_s&aM~5IZ^2kDXOerEG^mG6DWL(-B{!4u8f&&~E@W=2MD7;r zzMgcxl9HJuv~w={TI?T?1pY{Q)$YK+4mJjoVky9rvg!d~DmYnI!kh)W8{l$-IfZ|` zvL)N*65t#dvUs0sWIh19ROSJIy&8-xxnAptVD>cZciIntRtC)@2q|-m$lyTz*cr_CO9{s@3H*0J2HT;dG4jl&1%kAQwnVYaP*#T()i%*UZM8*B1zs zv2b2fu7vtl*6uy(Rw-}_kg;3?llJ|HE%=8o$E2Q75@a!2G?rgcE&1UOjL6#@Ex7^?OamtGC5kP^|JfWRCZ|z#D zU)T)wcfxR=SC$H+S8(1K_?>|IXu~2ZD=^3$?yhsoM)yyAv~B4p0jWDT?D6ouoBjg! z!)6QkQ^0^2=vR^HgAe9H97 z@0*vw&a+Lnlo4t7weO~$wGWUnCSCsAeeB4b5oqPJ+>m1oul(QX_c?nnEq~e;``BY+ z^~|NVcY%cIGsgOH4BXC{vSa6@pzGNEc7Zix{b^;*&)HRvsr%%=b3iP$y{U5NIf17D z>bz`I!4_lVXYBdA#&TZ9UEo{32{p!4PRo|&cukM7dguAJcY(iCfLea;t}!vy?s((#@7FAFk)LkPzx)guQJ{g7J#T zaNSTZT()679ek2Y@Y}gle%?08ovBpN5aU4lLMFiv^5Rn{SZvQ7jseuQ8>IEN4RU+& zGda=t8DC&>i*5k0QebPBZ^e(H=1CyY*8P*pZ3$p?Q0N6Vr>5zga0pjCCHZi0!b4^yex!Zyznc6_0m;Wjo~mGv}* z1B{nW<#C;EVA#82;+qUi6rU#>@1)?N0k0;~=M^9TQ&a&&-oS#Q007`p?_n%SVJ7#z zl*Ob$y#nC?J5-)H-D9wgSj^3ilr=L*5KDP|WDgg*DbLq7tF?mIDzD1dGV87`b@RRi zxQ(2)ylJlh^mX1F9*slW(%wbdPJw>0Nr1K%D5G8#lCQmjaO&%eBu6st>aurZ62OUp zsCK;229=^G|bej&k(chGu61stc=7F6t$R zujchcpaL@zk^8t-AXhftZLARYMO{X@&A7qcw=p1uS#@Of*R|;nee}ch{AWM?unleW zc;AozDE-0xmt-6>aAx(e+`ejW+#di0YXMeBbKipL4*)p)n{(6J-b9o`HXtedfc4vf ziO%_jUG;bXz}PD|yHHRe(yBX*vla!k9N8s505*in=gnoAWbaNSCF`Lid={`}HMq@Zu-@Tjrq>&&9k| z3D9f+Fy{+KeHj1_{-9T;xy(U5)yH?h_>BPYypEE=?Zy4jcV_@u!Tb*A6~-jyva7VS zt)rQlTOZxhx!VHDbe-Odv@qv&X7(@uUF7RMkolDi#1Q~^&EPZgvp2}c+v^W(i09Ps zZ)OzpZ%9Rxv}@aJs0n3mE>DIuH!3*{|JtmO$5sH~Rswi5&4UEORT0c5oy)xjkwvB^ zw>jqvo*w`>6~Kf5RBzU&QM#HG1ZT->k-c7krCiRIPz-_B1Zb)3fAuvCG;to$&H4l& zATy4Q6|bXE=9BmzB?>A607HserM!`US?4N9K9`jOzyjj)euFS@MLwHBah@9hcyc>$ z&L#J6bA~qKXK}e&W=pDMtVqZb?k#%}Epqr4nme~d-4Bp!7>s0q@*)*2KfK0S(|U8C zn(&;aIak~Au@6x`0L)Hv*0#7OKc_>E*EQI_GQZ9u2qu!$w#HHg9qr3jLC(59L=Ix^ z9|nLKJY09@U`~=sZ5#N<$9G!=y|>oJ3TF1o42P|dtO%|#)(j*%m|JizsQE`P_*;Pd&1*|nBC z*kq}Z>li@;l8cwe6SIl={2rX$PWq8t^kW7JgJs=|l1Y@&=XDq7tF8{nLRc#>l4}8j zLs=K~Es>FCMIMzxE)pMS!5TKlfov4VbvXE>>V4`E@tQS={yF zQwqzG?1uVtKkyH;rr~p_iSo}Tvt4K)Oc(k}0PZ@n^{qW26CR$^nM`%K*StPLPGyM= z4{J@@;QnY;FU)w8x%W<#R!|O}Bl~6mzSUPte4eNkaGwj4o}bo&K}-s^<9!5UUch3J z6qm9}p5>M9A(AkFQ@}P~2-uN>MY{mXd?PZKfX?N0$H0^Ho;i~&-9(>auoo`j&(!nsa}31F+b?CH?P7)PVnwD>!c#!)6o_JgPk!z$243DaDL^;YZ)4?7@1HYx z$AFJ1aBixN(t1pRI%5EAZX3IR?7VHqfXb;p9s~0#-6%=rnR9vFyp7ZQ0UyR9S+rP&G5Ul-D|B-^iW7p>6Kewx?024bu zA1716EMx6GWm|d9M?O+1`?USj@=i%er|0Bl8k6=;kCb&b)%H{OPVGLo(Xr3+x{ZOL zV`F@5yypPxF1s7EiM$_kzh`4KEnj+04v6G+9vlC;lyw;yF^=-!m#KTE#}+7P|RcxeFV7lMLGkLhWV#g1*vDD3ft8JTXuWVCw$bo9(tT4aOU0tJfmV%^~oK?)$H0D`pfPv;g&4W0wY3PlRpwG39 zo34mVUB78#z!iW~E{C$53T5v1nqO6XMyVU1jkIMYn~Q}$6aJ{G6fbgHZG-1|Nt?tn zn$kv6rmj^6fOL@f-+tI!Cw`8vMN)RX(R%9tbb}iHk+0!gn%>1tD(ZTAJPu{ldX-a- zK)onb;GL9nenN`8h=3^9al`Vg3Yd$!pQFZdjXDiZC=876sAZ-iTN6vB8Ut6s{6YZ~ z-zR%3QC&jUUA!^M07|V4!gV8o7hS7NK1Z8#VQ@iR!z&ZFlsYlc1>5Mn=4vigD!GU( zuljJQ!A%2*2r!x%%vG)X{W7nheN^5ZY)iJqMmFVr;bX2(YX_t1R?L7jW=tW#bXXK9 zIPGHlO1+gusevT?b=S8#CmTTFh8)E_UU?&WwxH&612F6))c3#nmT#h;{?7M5Qc}=A z|69L7-~Hhap^n|9GOy;f_(E?R_+I=E`ve99O-Ug1Nyqk7TE24P1p?8fZhkW;=$wM^ zPQY`vyU>QS@<;)*hus3#JK&G+9*Y!mqN0GnBCOH4eiB6E89XiLG=J(Yf;SlG21v?M z*|nt8V3}PWgXN+IQ0&&cQGN;CQ~|0A&Fk$2RMvHtI~356fl$7_I68#%GlOOA**RFh z3_1g7L(qnR(z9a*MdvY(JIix}U0e+yQOu&Epgn*`Hv3R+3p}(n;f#T14aNW{TnX43 z96G>hK9>Pqt~2`-4m@$nCdC{FS!t1#?-=l8LSp#4GSI0B?Qwo80UP03mZ>fd55hTf z@mU;5P^9}N9zZYF$cMu02hfVV#0&YHQy6rOzEiLt>WBAFo{QWDYlxzrYcNC)IpzNB zWeErYOIDch_2NhZ(+Vj+0OMv2KUgi6n#Wwm0i;rO&cZkdKlS2J12Q=sLU_jN3^LI~ z2k3vKR9Ib*NbAqRK*e(af_P!oCi!c*yet!Kz$<`K1P?62x+T~~djW}9usUIYBp?U} zaFnv9aF!1lT`l=?c}}Pgj@34X0Hn?d2m~f^xPPzG!whsWXe#d?1*D~Yy9U_Huk?+)$yRDe_tEb)WHI@+YHl}W6DNGxmKtSI_wzDIMZeN>|? z0N1?CBgK5FfjbRVvt^hM4rAbK)7-=L2<9sWuvXqbNx>^ACqId8mfNA}gk9}IT^Wq3 z=f`-4fTof&8Gy9BAZiK)Zu_MMNa<=-`$MF;B{c_p0_HpEW8XLWPAowjY_|;LGQh>m zM;W*RJTmCXz%>|@Bt8YqYz1*WB(T@Yl;l)gr?5*dH($?yEBL`|+YEwokXXM0NXW9w zY?J)A<$0|tVO*l0LfcesF3(|`)O^CVJ_~b;0;ete8`nhUI&88Kz|Jevx$Pw-#HL-v z`ZF-fzfIm303yqLmj?ioo6WhwrD1I;DkW$)O)tUyz-(8^hui~HFP*%TLT;Fuzn+76 zR&l?f>y?R5B0=3lX8KUTRGkX}l(VHcSr-UcE$zQd|038bE} z4nfXy?jsLrXRxW6!C#1CfjpmsO+>oef)Y4@g#E4sBZ&Nb305|0;V9;YCTimtL%<66 zyIbS)xFop_3-0q-Q0xLh)8|hCdUj6!**+j|*Jk~`>&MENA1MQDis^QpI__$dS9-jX z>-VwV#@cEZBlwlD6{p(zm7e!X+im*uuJY`=_Y_O{^Rb^-!m_;Xy&t3=57M^RX|sL74e5FN>M=HM(|_+$VVJu2b$s%5 z?==M|@4H`Kw^wrgE3uoYYxnuj>$c$y`1|M{ld1)gW_9(5{xfBm)7AT23H~B^+4v(` z&fOe;h{xAG`z*cxyT4odF6%GZ{7sZE?87voSbrk)qadc-zzr|jrcIn20#Xia-zf!# zd@djJ`CD@XWxkpJPU{ds97@WWuzuZ84;voxKIXRXnLpD$v-_oEE_qyjT#V(MgYJ(Te|Szvk^GZX;203z7XRF;{w6$iJJ zWvQF&8c=9JoE`vAuJoUj-+o(a2;?d62T*y?wKKmb46W$n`o{zS=Hiw`Y}3N}IO$Jx zlY;&R?D7E?Z`Tbe5onO!PMefW6=l=Ho?ypiu{Fm;R<^R>?Q;Yj#rI=fcUkUd&7TQ% z4V5Eg>09LdH2_|pWq}#JvhRDdtF4A$dD@!L>#BS_opQU&G$Pa3cLJ#1uM~-KcdQ{)(z2*q-;%_xHv@J7U!CS!kb`8n9IZDPP8)G_9>VeScj%j=vY zK0$p_T}MC`d5aqb133Tf;i0rusIN&Y`{ZR2pfO8DGicjMu23!?`v#}-zHSK5jI1=& z)q7>hwE!mZQ%K*n%-jXLl?DZmnSj?g2f=15c_!LS1JmqXR8>qwHG#Bm=Kz{rB|uEq zNScF6nac>4VL*(FSk=jzK^c_gAi=EB&l&_&i*I9q3(pSyK3n46!Vs0*y>YOX1K6e! zM{4jMlGdr)3IYRkYb0;8Q|2rKLw$RR z;6IA%nUYLR0ObVOCTv4=O?XDzt~KDT1?cI)Ry8iEhWD{_v?&+x)yzDdAE8h9`6)0c zfj`0+j4zJXRuU>C>m2}l!T`oRx= zxBDvNQy0MED}<@f_bF7;+GM1J9XXR>r?R^Y1{(XA^c|SnNdW!%5LT&qdKvs9WxE1U z1L!Qz9@v$wGJSh(mm-cYFvXp%j>TTV)^s)|1tjzeI>+^&wUI0J%1&~^a_-jt62D!- zfMr{2z~mIa!}a!%_q`BskOYMFzK!DsbwVI^J*<-kPOo;91dMe3YM2*eeLbZ6o9=P| z7oAhdZXfGl`%ePEx~=kaAM^F{o-qKZ%s_@;g9LLE^B-TCGqE1Apg&=?mS8W}@p;3J zhvua&WNzU)F?i1ax5V=dtYv0`cP1H6wd7F-;9kah^4LNDQy1CaLuhV^9LCIc2D@2V z*}WeE({G6s6cRBuq$4rvr@PAF-6cW~%k$emM{ z_)k%X;J&Pgc5edN3t`UW8)KaAW76hqxAeL(nYpR_iaDb&y;t+`&=@_Xm~F_y1& zs&0I=NtE>%c$+gQ?MTP_6caP;$J}Pj?sDm9V`Apg7#LeR zE$3K%;;DAb+aRsum`zOi+c74xT|ef>ay!VMHRUJsJ|45hu`!wl7L1MCDF*A@Y2-9Uu6>2k6;npQW4Q z(L7?m>0-OH4QXA7J8zr8S5-b-BJ-HfP0^3g_B=M^PrX5bANAwSU;8W;*U8A(8zgNmJ?)pC@C4wgMw!8TDP(q_MZk zU5Mm#k<2>=d==U>(nvmT%%yDKW-E_u!V;)F6T0H0Gk8d@T~WL*A4Uc=GZ>I z?eBdveeB~Or;q=e-_?ljUL)gW<4>3zk{{J061*g1!^l^T>bc~Q5wQ}D?ElnzntEr72X=RoA~-^%#6 zyu%o`x)E<1lfLa#>ax~7-abm^4(pu(XpV`^t3v&nhtDg(&XSylym9YTiJ&m~Wb;>6 z4=odzKs<(P19nN|6NwZQ?7>Xs`*U0Kzr6y6!T_~AvBG#pdu-aFZBWT3&gIycC21Qz zSS}V4p>qKj%06z}4|#&Q|5dudHe-9;xGd-Mhi%5!J~VBiw4dhSLykC7_uCEmJh6@4 z3g{aFUgsh(SKA!fb}?XRcCb{Sj}%POq_u^yYXm@$LRdaeAM=uhS-z;w5miYlWLrVdJ7+efL)xm+vJnIHK0|0Fk+2#mF*a%yQH{`&g=Xu+g;9GUg zM&_1V$1-ne@Iujf+MJ#$NL~)_b<93wMsds?DDxw;>KVkT4-ZrYqz8MM$3VmKzJ=tl zBuE}h?pD~Wv3xrWekyBeQ&At$O?hS)vlQ;hiS!3EA_4Mj=%4=bFCfU}@BPhhe5m%u za@24B@83zEx_2-7kNWOy$pPC9zR3sw=r7mg7(84Gum^wT1blKTT`v0}eY90DaoAfN zEC9+QFKnN1~Z=( z81CJdanI7|91wH45r%OnQ+IiRapo3F0M)bGM*>)eXKv0fY4yTOREO{Qv-b21AbfQ+ zu-2RgdKBs_+)>CYe*)K;L|sk5(s5>XL*I0ZLi~9a<}@J`UK;* zP==hQd+-3h2>uUq-|Qf?&C9`e+@r-4uR^`bWeFQ@{Uw;(r3=1|+q7n>)*AO}E>4Bz zU(W$N0wCtwdIta+=iYuI^v4>^U<*t|J9I3Qec$+l06u01hv+lZi*vAtIe&5IX)?%y zdX->R(8@Erm7l*jiV8j!d=({R+3yB}WEBGBe+9@bPF7cV?1kf z{^Icdg7uw`;3~1iF@w4n_o$v70f;u{Uf;OPXFqTd&Tfs-Vx(sbT2PkW_#NMxEJ*o^;%f-&uHXSRVl#mH7QP(2? zd|-02{r91Dsu_k|=g)){t?RTfTFD+VTRbVU<(%7D!7^6UkN~V2e*F-DT?LSm0lZly zlFG=T-D(bJdpXOxj-VO}jOETpy1EKF8 z{YhzmNLh#RVDXgZZx)6x zm$!48G6Mwt4q!H~{g?G2%E;%kJqWvag?6twr>^=1*aleEk{i0|P8DD;P&Nse(4ZR& zl&iz4nr@P81%)!`eFB-B<`|B{Bra70z`L} zZ5N|;s{F6y{0BXz+U-G}I|jz@D*G6aGSzQm?YIxDdC+p~V*~E%yM52u)u&_aJ_V#+ z_qk(jv@2NRI_$%-joa(mUk=PqjfZ^!9oMz>+?TvkKaf6qjQuud_pgMlyRRKziEqry zGsRrpSN~mQ%-iC+^?FdhwX6T9=997Or#`zbdwA>_yX<(%#`3^}sbHq5K%VRRq^WDh z{@&HDd9cF``1|M|v44jk?yLRnGlo$7_i)Xy3#v8|f!^(7h#b5*9*5)2$^@pw|6E~; z+h-O$Koo%iFq#s2@Rt8GrkH&egJidD%IbzAg1XHhvR-k@$i1 zep~C!D--PS5R2Zw^8RYHN63)Q>Xt9+|3)a}4vQlTN0sRaB^%jum z;~2wzI+6J-M%p|20^L-%R2p&Y*%;wXcmRtUAZWg^7)FWZjP*$>kEniO_mBOSyl10~ z!Z7YT3nBvaszD|?eEWfACCfjDpb^MV+uBfvkF$1D5SgOdg|>B^>x_}<;+j(c&_de8 z>X`%}Zcp3Fawq$(KGx<+myG{(O`WJC2y1u%6-aYv#{RH(3=4j;elvC_nUYl>`;Ih_ zQ}XCq0O#QAx7GpV;u^<-fQIvZvi9*26pr>&XioNI-iWqCerVIknjY@sm0=o#Np;O& z;I#R6o2LuS!MoL8@s9oW`h%>y6Tjfi)5owjGM298leMxI>3q2Do2E9e)4qZ>VXc>AGs(@mF!pZ=b z`()%T9Vdm&@0ugE_amf}xDwO=Y74@%w={j{loSBl1ftlM&gu0O*_&I7K=Omn$=AZ^ zdXR=P0!U0=$&(k=A!rTlRA_rE!xd#r(^5Lg#hdn8T*KM`myVT~wmv>|+ve3Oovg!$ zabtJgTAZN0to_A5_$}KN>vQTW%3+Mz+Ldf>acQCDs$vLo-98eYYqAf5_AsCAhIB1* zOCPT*%x!)&9=1kE1GmF4a znGjaqDVA>RoDonfEnCVY9J@X}#&Q^s)qjdLmglF;&)qWbm{DeyOE%8!k=)P8aW04oFAU(U|7bMySj={dRm zq`qaW&QkzgI{wn}n_i!`ao&D;F6*gdi%FE+CuON#2NWOsEN7V-Lp5)&w2!9pYv=XM z{cbwe?C-I$JqB9l<+S$C4||reqIUt9V?I8A_E;b7^E+en&(!ntag)~9#_B!>a&Dvf ze3bi!DLc=fot`t*Cu2UEk9WWO-8Zt&kHg^xzsa@+d(XxsIosQ{C}Fi5%b7~?-D%rj zkVt$Y2c4cIk<{tt&CT(sAJ2a6*P{R5@|L%XELu0nh}q_h7qbd8$dIH)T__l*m|>n)Rta?ER3pO0SFg zHSJxeR8|P`jFQ-?zTlKpCcaSeFIs-!GRa*Q6=sy3ld6Oapsu`7BCnPA!q!79_Au{8 zU#^AAH`g(5#`Sx5CHK2+Cfgj{gf^1AUd6D%*Q-Ad*SzDox6wP^_O^#@VmHSldH@9e zksqNi|0{omKKcCfkvWJ%H+q{!@&hv^-bCZP%_AyV=%ewCyaPfePNh!m9L+B;8wTP- zBwuAAIYqI{X5-kaWN)I5!|w*nvT8R`1{wJ?i&EKjZtML4>@b!dTuTnC-Y4%cCNLzsEkX_d{OcYVaRWKM242n1fYwMe@BW&ArDAnqtVID)P)S4_C#osVS{o z#C(SQ39osviBxhi`naix+=|*Cz$2KI?Ik~Cp>nYBZTO!_=*j=Mu3#gnuO&A~L)*qW z+ADm|`GL!r%TQTfk5RCfX?vQCG9*Kp*@V4dj}0(xH<3}K zWHYl6sn1{o?db*DtzOJkA%pD~7C8Vlb*b{+Ds_u-pbX@>DnFf`!@f8_C+sBHdQmTh zL3)0kynoByM%#3#Pq`2VOLx)44QJSJY2bzvl0P%hN*N<}P`ASO^o0#{uUX=6Sl^Fat}*iZS)(ckf^IARIwYT>AU>HuQ_IaoVc zW4O7#3#)=fX8=zx%wHbMn{ZD6cd&Jm?;sgliZphZJOANNe}sPZW4}$`_uMzr>z{u5 z3ub3O_nW^;Z~q7HrBlxL3n^grIia8nme*woQkDm1|Fvmlt9mgD?J+|g%$F9BAr_W7 zWO8EJ%>L)}0 zEx-dK%`05L>d9oT(7hkvZh{L%KgHDT+gBY_3b;jesg*vlHlvLFrq_WGHaJX zTdoT;hc7?(IY_aa^fRWga1P%0mNm|4n6KiZ=cnf1^M%Z>UKa9@B5u7X8Q@MQcxHWg8@XUvLb{QnJq5Zn>eYZX*r+H0h$4_YP z^B%I#Ewh7}q+NvhQ|M=8i*L@I+pJH+&l$~@w~=F)nS$_xyk_V8$jc-0tJBby%(^^) zBr;1UgMC-eb^ZwbyJ=NXVJ=e6oNqal7x{gmPI8I)pTFZWkpk76SGH!B^u_%!4PW5> z1M^WOvdDNJ=^1>PQSXGkxVpTLHkpOKE}1DB?!l?SO%fmFZ!U*m5=d^>kYDD$Xjd=9 z`Ga%bGV_vIl1=E(qEKmn&MORA^@{57eLXu2{^|mCoGtHwg~)kUIYmXq%wb2W+9IG_ z*Pe$lGZQ?C?J<-eOiYXi1<#9G17j)$o(fT63jbSgPKALP?2p;OEay98mU3wSwyzWb zG$5)(9$nO}Mm*PAmit66@!ZzYeeu!$X%X`o!iOxWfeY)s3WdUW_2%<~Oo zvIP|6T+cG!&hh*{>ZK5#%?#xhGQz#GfIBc}Wy~E!|J5O%ujIl-?&iLrrdy+q+|LsH zC+mThdNR3UW>JIfTQ1@AkY85czQI^#yMe564cp>+*P+auyO)`=1sKGf!}v_t#dL`N zo(CV&v}cm1xHv$0`Q9cUT&NWDra#Bpx#()`=g?2h39Xv@G^-B41}+qUC(Qv2xnX_B zhtdv$#3Dp1_8Xqja-LZP2MjXBU@~`21m!oqyi*^{p9tfklQOc)4}IT+vbW*-a$Mt!F? zQKAne=Qvats1=E3FJlOH=`^RkGR38ARScMs>omOxf+%XNXNM3zVT8NTPjjcwfy;Bk zR%do^$$7@t7!L*7g6-xAdo#=&?pQ9~B)xeY*<3vax{a}-c5U+S0`I1d{JQ+RefJmx zdiSxg#=x|xbFO=gm34{*HwH3Y=blr{w<&;O7a%fr{Z#+&E6-Ft()Jw#`0{h7>X@H5 z_1(UH8?(*pw1a)Ns|-`X&AxJs0lic0HP%LBVBpyErrL4Wb=SFH{_kD)8@uP2j~Tn> zI!xYOV`Sg4&)#;~#)IB(>hoRT<31megRpsEz;%4YK7VsvcI{Z;&8}dIv38lVqcQv6 z=QsAvZBzX)6;SXX{W^8dgV@KIzuh9>9O;S7whCt4JO-N+|@#P@iCH^n~Ex^*)g)l;$ATdTj;V5hXrr7$0^lH(Bo;0G$0*DzuBlVTptUQhjH!&(VGOG~7Ab0c${ z+rMqK>H1;*A-^?G4docx&+g5vM=a>&LuWNAhQNqJp5E9iIszyFuM?%Vz@z4fcVmcIHg ze%T+XiJhFE(|3L7gY=Jn_M-waRhm1K{XkXfe69>W8LR+LjD3R~t^9A~45hf-PBB-L2zSjT>uo5drHagPz3*~1Z2Fzmx zOf+z+v)YtZXN*~^Bc$9pcW_xoAJtpRF{DKhkQ!{+XoCf0nSCjF#t{TY z+v|4#&^mr)DwOfbd1#R{R-=vu{2s>t*4h?6Npk8sW&HIDoL0*N32It%**Zxvp*g}K zS=Pwwxkx#pPF zdAwa~4&_eU+5v1wa9H0)KP~GcC;4oVkG7VqwU9d|0XH>(wX;kT47vfN98%FW&AIli zDTHvkftlL2j*a?2mJ|I(03@{&vG;oDXO)TW6r3`901#DFiPe+J5qAn&6_RTe?Ovk{ z$Vse|YRs)#&Q)f6MKL$62EYLbvw50_h9B~ua+~xj3odJ}8KinqYp=OdN!C^Z4tCla z0-Z7N=88cwyw`r<|0;m_oib<%{U*%Z53!Bx25w7cfJR24U?gOo(K}i` zeU~VUS$vjpbj@$@{cQ;@ZR+h(wGoLDWt)g=V68RsI|In|GG6_V0!7?RN5-nRbywjc z1Q}ZgGTbbWU(9rESCLHs-WbH*O57XgJg$OSavXj}+06j68T>_F-Y~~_&AqGuxq;AD zQ3R!0e&-}la4e&fJkYZIo(SN`TQiB>=0J#oh zsFh%ePu`OHp(E^~hyqAdIEWG7JCIHc#R>YMIQepPR#QxP63VSGj=iDx~{vz{LP;s1Mw=OcM8a>y|U%(%h$049GD6VXBYQHnaMtOuV#>5TTUk9Zc@K?^$El=lz$So0e&;{iXn! zoE4bgGwWyRq?`GLX}IkFmnj+`GH_JGbkc!J7Mp)c*5!o?^+5)o0Ac^82O! zV=Q0rm|xh{ereyN_G-RutgZ6z(lI%vt}vGSF|Sk3=pOT9*JWVmeKOY0*9DK$=T6Nd zWB1+_j5Nj89vgpon@)|tv9?M3lg~8gZs497s_-~QKE2%7c0w& zhmFjsq2g(4jM}Dr3i5%i<%%nId~-bd$J?KKj$VHGKK+C5{a)R0Rs&JC9$qIBpvM4` z^cZ}x&MA6}fq`4e%W+voTf^(0DVZcdv2055i+C@Qq`awRa(TJ|k99vbLM*SZZX6{H zLh5WCsTaw(i2Y zRXL15FJlDTY{~osiP^%KoP8+AuvU-k_)Qbb5#3ZTo6bI6#}!xonC$X8)Td3~vD8D& zX0r%{aXBqduSWz|616W2hG-cK2yM1Jx<$GN8Uv=7Ar0`c7m3xL1k~q^d8fdY z*Y+yNKvEw8Uc8$#8&<~{`nb>>(l#e4Tb4KOZ9ZXEIy20$K|HGkoX|~50KB#d&Fat# z0gNtRd`ZSnH87Bo-!YN*EDx4o6mu?9t>z2m zcIP(e+8%KO{Cv)5%+F}gS1JRm8>>z>;@PeMV1(y!4)Joi01MXo5C1L%05(>qt7Q|t zJ73|sbM&L5KX~aydhUJSLI2mk`Yrm~|LtF=*FXK5KXmK*wU7Vb^xXTu1Jch9o2Nyx zo(^@H&(MbGk52P5hJLL>J8;|Zm|`%T0nOrYA)B%q;8MimLU_qDcQ87!M^X%3$1QZ}= z?yN!%VYG4ZgPb3mS>j+`*1|}GEnZ&2hUTw%6|Fz_60JV{Qm`>$z_x2)0nYB;l0M*Z z7CvjkSg1LlFSE=;oAP*>KXD88xBBc0w0QMXRNXqF?xmMe8D^`-J`yNQB;w!z9tzCp=P|Cq7sw}mc_CwTp<@j|q4*yToM6Uir@$1q4S-g@ zzLY%4oF5lV;-ll>17>K$c6}MzI{2^P$9O)6zYou8PF@zaH7iMU5>zsO>h+S3vXB5y z3&_Iw)w#V|ETb>!HYP|J=cwM9CvF4n*_)OW36U({Q}^2+X_n>_v*#3Hmg&W5%!pa zu`VwbXz%9qMX-1Iy@t!dcC%TXh~0)~mNk#%BY;%QVCKA{e9!setH7Y;dow`Va!yxf zsd8XUSu1L;et}g^LK#cs=T+FOQc#@l$@z0PD*&}PpMl2k-SXhc&=#TH*QfD4h;wO! z^~!Z-cIfrFN3MIuo*jL%eLKyinapCD(kC~&M!X~ujet!v^HDJ|NeBDMExMl~!sCAkfS3)2dj6{rxa|$XK zxR$}@)#Zy|vGR2mkj1Rosy+;#pVFp1i{P6{nKN_Lz(E9DIFB~u-<)R`Weopu&eW=0 z(&hR^lz|zKfb#S`5^F_f{`LY(^Ka|}JD(k)-Y1uzR1DiBj>{OxHLs6R|269CqN)~H(ER?*tlkJ34`Yp)!v*q= z^O~SQ01M|R-h@8nc~#!QXQ&&qIfa30d0jgI?fiGcK^`RFD1dL|C!V3tB^Pgx^-FwD zXIoRy7C>>bkLm^ubS<~etE8YN`^2Ah*gn?nIl$k;*^?+k z>(>B&d5nlP^|a!#$ZT}Ch=IbKXBdp^>oAdavAa?k)BKuz1AGi{n%Z<91BmSy16K3% zbGB5zVSgRg@D#9+mN^H-_JIgvZ8!xaUkC6?+jv*m(|e8qHe>xb24GCJTi(9c1?dSmQjFms{+o|*OI_wHe7`w*$dMZdD zFLPejy#LdCj`daA7gHek*z@yaS0KRD^R9E<7>GI5{?`GZ^Y$CFt+8>q%STNGO_&{~ z<7%wl`#{@$a@RT7zpEX`$~;#7G4MZsUT%ZC0$+059J^oo_gG(z^?UmFRC)tz!?9x* zcs&-JkOxqG)mMGh4fy*Q9`a%!n*@bMqmxVX_hdFR&?>j?u@%5W7G0Uxv~F@LhCzUFS!3j>}nGK{cR$v-jnEY*a?9y`$(N^}c<#cn-;*zy&L{Snw_03%ZxTft> zC`(I%Es9aUnjTy8oeayN&EhyU&{qNSIK-@e0%8@KNAdNqdo8{Hec$!4P3h)%%#LSX z|9bl2@B2P_)7O0+EX&3yA8~B!zaa-tw3TBJIj}&U8FCx)VE`=7D~3E-cEmM~ZeSUk z9b*zeua2WMNJE2~aIQ+_;a>dHZ3j2ToP!#)Qp6mC9w04PQk1>(5XFFxQUP6ID7wf} zMsc+rAmwWgtM`M-Krb7~kj>@xQ?^!bK(7M0qya3x?=+8{loOku<{rE`u5%y(+sVc*@@j^kt`q5ANFHlYH8w<9g0t`AwE$&RW|`vqfaE3RYaxNE zxjqLbu8xzLZWK!7mDp?}8>L;lA=}6XqqO%Cyk#K65ykny#uaQ~EeyKZp)lwykRpAb zu)rgh&PA{igI1x=%vR-1Y_I`~%=T&xT8Q&gx^O4}>K0YPS}zmwga%o0+wtbF1TeJJ zSNbmRlx5#^q&wFFNRt4|US)QLX-f#e61HM164r%`o1ggQpQjK1$}iD>{bgT4U;QOt zPG9^Vf6?bv ziEQ8s*l_HJOiFnp68h+01snl#u7|m!nAZSnAwABvz1j#^Tvf_~ECISR=*nO(_8ZiuvHBX!5mO(un}+A zYuXS5C2E?0xPA@K6qct1l0+uG(;y%V7J=Uo*00RXLjV^CW$;nozDuOf0XVx}L4KT> zwcY87tk*TKF@F~AT64Pgy~wf(Ghdj64Dd=IinI&gliQoGhhJssly;RcN(*De zIg0+4WqTky?#;IfgIn^t3pnIE2?%4LRTNZxl(d%lbB!PuFu#>m9M>O^h7~rc_}OlF zwiB?)DLBV~j$phT9J0alU^hrlqiYeCIp*bAyB6jh%kAP;rT9s&Qrz&>rAoC5xEJ%y zJI#?=lrbkYK2znAdT}JI*6@t7UIH{TMl6>N;9|ESgOeDj7AB~s1>trI_hf+Ffa|yx z(=}gJiFC9{FV}7^OkI-p0;`kPN}=3UIXs`iM)Ywn?Z99d09d_F(q{;eE{s$nlQWk6 zN|7QS7FW_v0B>CvKO^P33Xs_VppC)r%4!uxau2Xse0ry^mR26|A9Z;MRwRNGy9;HA z3geW2dIj7pw<-eZxj($vM}hTQ2Y|SQ%&t=gwyc4{q-J?RxWg?GytDbr*O|Al%?s;qWwV*B-D`sXq2%qRfDF!HH0_AbEda#(4>;^x(h2>g_|>mbi|u;dYw^TiFEL;`L#v{a+Ol zfXARegWe1~~(a z6fl{(=U9H=9Q+t#5>DkMPT7UmsrML2FvdpQ1!m{v%-duPNKA8|?km^64Ys@=#(?ac zRl5sVnhH8d`)2I9Q~jI*I#c(_%bVUW&(}Itw!9o;?7(z$GqvYkK>QdGHdgj2z&Ov( zJ2rlH0ne#D<~|`mKWB$pyI=R10t)lG=eCslhADd(d&U@eJ9SOkrg^#2vW|hpxt}l} zHDx0yb2_(=shsJi(vSwQq~DGC&-~wGtlBC2n*vzV@6zA9K=u5v@>fX&ef~SG;}jS? zR<^0YjInd_K%fWl@l$0pcD#)d{`(DYcmuuXJ@28LggD~U2(`Mb80?@E37^4JHn>vc{yq_Y>MS??6^#kqwt&buue##~FfA)&ysfmSZT zvkflt3NT@VUEYW_XYl|SdvC0~!PZS3BvtuH17~E;zd8?%`Ry@K*!CR@U87(l#Hk#j z_fbI070Eg(l;ux8^9=p?hdu;Z-<#v+c+lf7eDN34b8mes{m<`vA9X&nQLl;*%G^O- zH{K-X3%xQK{qQc;>Nzp+!+XtD?c=7lPZf9!0M@Z3Kx7f6Wcx&c$liM*tJQ)*h_>Dz zO)A-*B|smut16t=TE6q{YTz86SGXuoZ4vdjm+WvV$W#ie3+zqVu#5mVmy0)r0U9`! zuqDZ>T&NVCg1{`bChQa0w6V5w%0y;>1Dnm9Lsi+$sc*5lxitw1T66mAn*`4q-!5A z&`Zniojq?cG z#;h8~7C_Wtt^vr)fC$)qqKW}l6R8BU)lDLp6mU{7Km2|0)RQO=vujaiW;He&d|y?< zfZl{UmH>qol7o{05zf)7;JB2n7iPNUs|FaI?b{_Fh}+szz*(xY!E8nW=eqNk2Bi<1 zycG~benCE+^V({jW+-nkbK46mpBb~rC%kSED2DM>i##E>=}-KhzeFGT)&GNDd-o~& z(l`Dm^q2nA|BU|ZpZ*W&*1_RkJ9_?w&(d#v{A2WMzw@u@=l|ue)BUp(@oT)%Tlg4c zg8otle^lypUSy%mGRhIlIzd26s3Y=5`O9Zg_o9ygFoU05e`XQ`JP-FlUheY?0GF(S zLh5U|FZ+I2>=bpiw_cCL!F_kYOhDP-Z|_U)Xvh7@064RzRe6CK(XjvJOmlk^7_M;^ z;N=-Q=JW))f%#|l)YGB;4`}n^r$q|&)@^BHmbq?q z&QK5&vcjamI5S^^Uj(Sq3tJmOM(fK^WX^>S~+$n6iwS4&~gb~>Jv;V7?gx+c{5l7UrH zd2KLQ!*9-u3kJF|*U76~bAjBl5`fUgpg;J7aDQg8@?9IPx8y$EV69xLWVI(@4)b#@ z&o2X&04qBINjLpPT(dGTUss3JgwG^}ib({Uh;ohhKns=t0A4Jj+w1}6bx^fJ4q^o= z!ETR2%cKDCM(6CAK40?6Islhq7id%NW3XjQFvGczdR<4DIt9i!HOQL_nQh1giv+Ud z%vAt5lnFVAHSaNBT=+89ztr=D$=W96)$u{4+876UT*NViyyBrhI7f5Sos&(pc0RLF zd(FpOc&F)3B(JuB?>%e=7|Qa10K7&PEaqS_1i4e4*qea>yRtnb9J#rDHL z8}5gGtpL2kc<{=ghX3Z=?VD57xfX^5&aVV<6)g%XAFvnga31 z?l%Qej{%VBz0x}6K!(*LEn9k@eZcS7=G<7Hr**Q=Msv7j$CxbiK9D)J;hd#6Wq0Y{ zY2VtWf!Sm(L7dldtla7Irh*3S8MZl^)?*iVoZoLuvN?bLuE2%dr{q9neokKBseZ`q zEFCx2uBlC!j~JrKhK;SM@ty~~gm6ty6H95>N+r()%PAPO|=Sn$gLTMjmgkyT~UjAUwt* zrZoqe*W8*;*@tRdn^qWDQKnuu>ztMan_VukVKd}HUWhH%oHzN`XzK#lkQ=02i>{9^ z;JxO#9$O=g1u2cKEAwE|-sI(MoF*)L+guU}+->PtuNxOJ2Wj~It>5x3U~=9ZH^(bI zo_ot%==o25iazw=59>zW@E+}T{FF%!t*xLTBFNeCQJ4gD7@Nj0p(L-OfxI$i&4zts zxndlv{5$@(KwUSyBggj^fH3YVCMj_fk`GXUGK?LTN%cr}yq2yL$V3Q7QE>>uhdNC(qFbqMKMPbSU29g)pAY=9_Ko=6mtYx0G zb2+G`2a{B#dmj^aVH)h?lKmG3&b%5VHuXAbZ$2adK=QPjS_QLWNr7O5C~F90~1^mKK&MB8F4G`-H39uly)=Ypk>gUv0M zB5$~Egc-*mU?>v+RA%Bav+r^(z}2iW9E$pt0JOd4NX15b*8tG5aqe`C&yS8GE38)u zVjdfi=2W|Ny-R{Q()A#NG?MeQV*6`Vx|#v2Fcvl}VXMIv5?JDCb9x>n=BXI+;K}gh za)tI{Rx1ZJoQVXkXE2mOpM~Tr<=mzwFWuK7SKD2zWUL{#rIod#=Rf-g^bddPC+G)% z<|6>$UiZYS==HCD4c$3DqFY?|Qsg~P&QIz2&wY-5|Ao)d>G_$gO)6z6#ys2tzL(0l z#`D+8mSbRhF6>bYW`SaWE>gJ8Z5#TL`!;K|wg+Lo{W70ZJ(T zP^rEu!i6$TrzZ0?_ug~PZkE>=bI!HbS$FSq@1q2%m%N?2y6>K|o3-YetJ!PJG3L2Q zaH37vrF2X}->EcpNcV*cvcQ|OGa0AQR}rAlRFtG-E?mts>U;HRC`eyiIs>=}jGb)U z@bEJF6YeJx&}b`yM+lq&IDEE|=`Gq6<1d+*z2v?nNXNM=`gUx$`rdh}%Xo1m^JOEx z98_AG(zDOsBa69z6=&4IF~JwbOvD z1g2>4lLaXd2v(&ClhDRJcVCF}$@x73&ihAi2!l7;*wqLCf{e5VUS3{K;B3ElzSB+q zvvU{rdr9Z8dqhySySPtK75RIs)v2pb&Q*HeliB7H0Is<@V>vkQBhPUBH^AO*f9dw| z^!3?u1R^`!%Yo5$LN+XeufUXz_1j-PNb@XNg+rItFU%1ef47@EGJYdCYo%ao1<)V+ zAjY!oB?zMzc4)JK{I>u=U`fiFr6bTUb4o8`Y9%avFoIFGzT1gRGxBD(a(&+ms4M#} z!mjjjT%{bfvPApcBW2>=lled1bGLoOv0CQu=$of^X~&@Qt^j)0r_V}VVHf!M=+hXw z-`&@|&6oqcf_(tkSHh02>h#{T+o>Hb4ov8Q1nf~4$}g|Z4L2mnj4}lPlB0}Y%&AMI zl|6l-g@kccYh2PuKq%;cV2C2H3}7~n?e=xn4GdxlY}*nb!>mvfP{O+9uFhMdTQ_H{ z2V}y70&k;AD1$i(X=JYfM3+~uOTY<$Z32d^zAIY(RHxF@RT@XcrqXT%z~eJ_{X^+{ z@uwpgtw1tUZwScTXrNC&UP+!=0Fr*qAO!pytVsAbK{}>L65k~_tqfZDR;P*(I84cH zR39@*)s#9o=w0`6%DhHKlqSw3PBk*?J@k{x21Me=t| z-|hMb&Z+dZ0iN)C+!w$;-;0qW+6$}Ht|v%{wnu;P0z@LHj-S~+0Q2)o{|4lYemm`h z3M0B=Fy%_;VXk!CZoHt~z(55+*;Z@avkGLB(~fQ}06szdy_0;-tp={H6!@2X)2x%S zWC7;$IhNNN?*x!z9v)2rd)Z-`ExhIx4rCdlzgoU#WvRPHa(Fw*xh~=bYgI@X7$|^t z*hYJg^uI?0hY_GL+B#GBm?0&NbGH3MVaSuEywZIP+0R(ln-p{p`ypU%KRnWPpx8*5 z%r$MdY45R=7yCb_zpmdClDQOJHefqx|=|>6 z#3xWWSs4J;zj>1Mrv5DeZL|8$GU1+{RX4jPvIJ)w#C2 zEu-|NvX#JG`FpN>kL&-(>b>ME<=C~<*8eVJ^KHhFW966&+L=4&wsyHqzgY@S$ot6B zb!C~$=N}v6mi%q5+>hHPOMx!svvF7%fB1)g_$m1NgdRC)cJO&I)-PY2eq9U1^3J7= zP3Pw4+^5HrdA#t#3+{jVp&xP|{LqK^w?L3tFwhkED!+ofHv8;DuMx}>`g(Zy>>tK3 z%)j*t`pz9=Fi_Sf0J?lsaFc&d(2(tf&okKU8oqqt{^r}+9hT<9xj?hIV|Gq(Qy)tJ zu!825^(oDLWy~~V+q2(Adwe*y)z|;+zx_q@f~Ut{*W-tN;0N3*fA9zH)z@A-Tyx~> zt{dK8O;%l)D<%^?F=Nwx3_#YObnn#nvgca9R8lf8tc-S zr@tkid6mjZfVh#3U;+d#TdOI<(J3&dld_gyKC}Lum&5`{B+t2C54NL%Y{VqA9C^J6 zl2~3wFn^IJbx;7*T@RGCe-lj4+vvcy3KBCPd{*8kB-YF3p~>VjzNZv(PR7^Cj8h$R zxns8NIFcPEmx%o-3ENu`khx0bCdQZc5etN{NA{ZYcQ62;apS&{&EYZ4U=n}@A3N3j zg$S-dp407K0~Le~h#xY)D3$0Af)<>c=DaDmPXny-_X5iUoCRwL+qM@LSv>)>iDu#s z??JzfzCsKYK;B^ex1J>3!!+;leC9V6ki;6w>s*%#GN?=>z-2Hp*DD1ee3I^koHFm* z)XE$dAUb%!-D3H7PhCdiG4_^E%2i=5L8Xp;UCc6;$l^+SUHm4GTdSNmYTW=?iUW0g-5_0o@(m4^AR zR>r1DqUv~Nxq|Js!eVq&-?u0g>+hn*00FU?+5@RFa_W*8N(bxDq)}+yLspc0+0K>*uJhcz_FJAF3l4xYyM#Z zYBjI3*>F?|XB#&|n8*3K@>(E0oP41iv=x|yQoccLTBY|XE0Lu+aAxmO{V=U#yfTUdfp@aJArm`HbDkPdQb>%- zNKWIV$n)B`HmbcUWg$;Q1PcTfu+P?G@_yM0({@xT<$CP~e>pL%DGd$ZSd(RGfdR~G zOvz`-cPsajfC!Pvo%*0L+GP%q`!eUQ6Oh?EE5EGGn#xk1)Kg7EFL`cFR+{(J6cc2Z zcCM`ryi7B5S`!A9D^6n#a;rMCNuz6xF0BoP?Wt?aEWfs`Ic-gsKw$MrmjY*ek_)yz z$7g7;GTN%rz$bZbPw>*_X|oLjk!;N3x`^hB55ucMOJbz1Yav zx=Zh6_jZ-~^=MWmSZftzP?Z-}IHlK9W}QJ8{OX5JZD)ZD&dXW}=QYZ9ZsufCjTN`F z(Lcu^n`3h243;?tZ+?$DlXtSCWa-U;h-IVZzy7_r?GOIaTc@Fr_&&lh##0tzmn*$PW+B_>alS|tk1A$6_H?Nzq*~+#X^aVP% zU(+<#{AdX{DQ!^7Nhb-;g$=<{g1W5796K>DOAaKJ{y1lA`*+*6>AL*dlAV52P{tfc zRk9=*@ysaK{=k2hj=B1pjY^iNou9L6me`f$Gv?T_IV&z_Zswoe7G!%hROMqF)UvAvtY!KRur0 z<6FP={q7I{@DJV3{*#|eZ7QtVZ0vC^fgSczlNfv|g=#N#WY6kmYGuqfysV8Oy#RSv zjLjLaf&Z`dg^Ll;jJnnF+n8u5hrWbjW6(BE?6BuUN|CZ;yM4+nY-4QNpwK*>mCEU{ zJjMDsNmyq?c0T`K16%6D^%|vFu?xKxi)j zKnG9=TUM)9#sT(q-Z}MAGHq1C7n{If2+2mibE}oG8gL(OBvJx#;HK&7R2VB^BkR(> z>(V-usxq+GXq(lRXE{juvAMhxHbi}PO2EtS1yCL`fCubg*r-nI&o+U+eJ>0mNa1pm zS~sNgz#U3Zd!MH1z;S!pV^Axz?N099W8KJ9Meg8Orp=u@vZ=G*yC>n0oXU-W5z$7g zvoitEl!?aSx*b)9RX{mUnQjfV8ARr}Jvk*XadrPb{SrRQeMvH2x0I$OlMpQF2g0C& z&)4+rR+zt(_tn6Gy{f%I9qV$-A29CFEz)z()C&~N=z!Ll8TKCT`uX5*f zm~AVtoZ;K>zdT{nL6$#7{`)IShm)ytEcIz*D zURbu#N5-ow<`lM^_2aX5WGsLEK7ZGD&nRO|@ZAD0m~(e<`}>c`v_}~>Z+kAb``zec z4+$DS`Zxb3TRLdV?*}D4=To=a!@1?(YfBIQg1Y566wzH<@eu zbi+Q|UtW-5DtVscy_?fCUN_G?M^JVDdi1IF##QLo0LmJtQoA~T1O@YAUxHkC3ub$K z=Ea}CM)0t``wZAhjpB{E}Yvwh~wtDfQi_n!uW9oR`p)`fESRlb{qD~=KOB7%Z5Jz`HH(s zclPD~9&OWaAF>?H`W~+h8mPkg6cWy0c&<)eckzbnF5htLb72r0lhaZ9e#<<_PQe|M zNk+LtY)i;igT)3ZYn;oz%5{u@i#iQkeBky9uE{)%IeN7|B~!NBUn#C) z{MYEZ`$uukxsr0A{iCgFRT}8PARZ;K2iLSG&k)GHdidu&r=pbd)=l!bp3eeEK4@N3 zFp2??LO$6cuPFQ?J`>lsYZV#PuY2+GEwb+)YETZ! ztIS<8M=Ju+0KD4G+bE9=Kx|!~k~P|qK{>kJ{(b_7!J=F>XIUE=@a~x#cmvQSx&q@{LADOLeBq<$6Z#Gab|+>@Fp()U9oquux0SHF>w2SW>$rZ^wxx8k z0<<`{;ru20#`un)W-M2ud5ZV^tao?UU&-HKpspZq9orVt(*#|SLzpOM~9(QUY7KHPQmD*)vv8_FSlYem^<{6~R=c%Lo^6Bf*O zuv(>TJ9pXNr)_av*S)U5=NRn|A31lO%82hNj~wq6%WA;C?IVD(i|en3`&4|XoaDBZ zJh~gUb5n^wV{IF#fq>9&}S58}-G)zI%GSHIA2G{;2!)-}p`U>T8k@IA^M+aXx4ew{uCZb?)Fb z2NAr=!m_CFFgL$@CsJ7lH%fsGmAzZ$_ceA#=Jy8fI5#n8jWHyf1%9r;htI66{Qj^{ zCFWFyrnsd!R(_9RO!e?te>1>YfN*69SrC+7Yv8vrqu%+>7u-+%#8150uJZJFiyiNI z_gA}*eBbxEAN*hcm%}k#H$#kdN_)0>(aRGK1;A_DP++4Q47^bcwOlFfDmoicAJ0sX8=6JTA6Sgsa z2Paqu0!cNn${4Mgtu27VB|Qx*w&!Lx#|p|(3cC^#s{{qCT!WQOWu3b|^_PSG>s)FB zpQT2yIo;DPRbmoKGg?rXPxhuyjG|v+V-NhIGTG%yHfkve9mGD61^1eM!e;?-WZ@#1 zWX!MEKm&li$`TcjQFDm17!`r>>%q8{wrN1#3rexle`}HKbitTvHU^CH4BQX$-~@=T z2RVPq9zpVrOEWaOW>WhaYuZ|g07#qW1CbPOC_&72HD3<8W@8>%%^BQ_ze$>cfrM~M z5ND)GwwVRHs$4k(c3dZ$_tAGn9$EA6w!#wiDz_Xoun_@0fx*}lLwTBmyaN68w5HQM z-NG0jrm4>QM?QDfO`3dz3&{>Hr>$H6Q-9}t+lbtC8e>MwlWX=IMIxHl3`rJ0SZE38?*rw*}wdaaN2OOBu8sBa;fzuP%wJ9>h;;!i!nGJ@;Khl!FBkaGH+Q`^Y1 zm^0l;?ovvw8c;$&wGkUDyWiGEDQJXiqE=*q)vlcDIkaiI0)mB2CPS;^g|dm$tI()< zMO`&r=Z#^hDJ+IAt?PsUn<*YmUX)=GiBeCHIVle0bAgTdTz}}es?Axk-KKyv2224^ z3%v&G2-}bXz{;8z09?Sqf%%^8IXOvWnhVo=k{i<8YkozxG8vGWI>I%7R+Ex_u55BI zd5nFM6fpL*vNBmltI-68d1t@fG^eLB8sSfTE9Jrluny^EXNn*IN5_;$R^FSf{P{ko z$Uh4swwm%jhjE(U5<^;9$Zik@uh!iuyVzK#vQCz98Nesxl7(R_%zR z^PAXX)V^!$k7P$<8B+pTWuCnR=lNjP)G>jq@=R68Mu;>ln(fB}+Y%fVWLVnWPwFR> zmktteE>heQ2p+V**_bF3MVR_!u$C}pv5q^wkINcmYzJvmn-4PYNaGDGJOl|WsFWRU z=>mkE9<)z12Yzegumr)1ea1x4Fp`GmK6f(yNw9s+`js(5*7QCx%^PKcPwP-MvbTFr z{_8`n!TCnM8&h5Ds#ZUfIiZSnILHB99&_yA^4v0)qJ5ULymGcv$(pm=VL1cTm}EJF zEPrmvL|g(!=h%%UD=|Ov&9wZvimn!XQ_A{3EoZXM*GA#kA-0nGB zbm#m?TE-t4MBJ-!GB`v3p=fBf(6Z~dqLdA8B)rWfSOa!UtB55rG7-N-zc zAq_Cl7p4i^)z{DHKTVY>^_bs|IyYwfW!yl>Kv-tC8ME=v@+G#CUud9QbR{oHqm+Gr zwUzV1Pyl-n8>UWjccz_%N;lArjZ+CDBxLh=j}0Y#iL8`?hz($}mBGMs?jSJ2tL@U7 z+)wKw%f)G+jn|x;7HGlED<{fJ!TNGM_oFYpjm+=l+BHY}VKQ`2V8~!FYkq@N#tWy=nbW$;P=UtFZrvY%Mp)ruEnl3GB+>Yq*y0vfQPZQ?TxGGS?uFu56Sy z8I#wfjE5=s$VuNc5NPOxfqbww3d-EikzR*A?Vu6)(;6F~meczUZ#=!yfUXN zXpFBqxwlCG^7^JeSYva`TQ{V@e0FZy8?a61WH&r}5ZTIpzhn8>24KZi3N*TKVAC)4 z!)s2xv^5sn7L0ZQ38LSf3OftzHu}{{0Vt<3x^3zTq1jy5#oJiZ*(X_&v z8@IA4*Y$dR#eBWGUr#3Cubx&DA>f^UN$SymsXNnc&y^*O`ig#nAg`BxQT-# z2d@trz!3!K67Gs zvf#l4edeCb$+%Wes-%ax;X?9b*Uz3i+dC$h81Gk|h=g|cg@;^k3>Oy(R5O5j>`XyT z+7tPW`$t!gJSvFa=`(%tyZR z6reINk1Xe@?~ZpQMjHg_a9Guk7IObvdL5LUPk6N;>( z!P>4h1`Qy%8>T=e3vyBrmQs*cM01);r<*B31dIfl?>f~{q9IF z9IRLP6WRtNqX|L@Z|wXf4Ve+{|jAV4`~lA|wUor65XL1m@U7GNl^Vp}kWeDo)r zyBjV!e!!1v1#`E`K$qOm@w@6&12Y20NlKAd!t7ne?`%^6X*!^OvhD8E7I!zo^6t5Q zRY14O!EWr)oVeCYkV{a&yB|dAeXL~Nw~4m0@;3;kajS^|{5UMY68I?rwh}Oz12fah z%`K#4Z(_vdV9IU4r!oS!1YGmFER}K2Ci!!7Flp|bvRq}kkFgwcdEgREIL5#&0m`|u zmEh-X7;-l;Sm*3sf=+W~J=XrWW!@cQs?N1r{_kVK0wp*(_uM2eI0IlW-7EjQwBsDm zd|Re%S;k{5>p43vol|~)ll^aE2hRbAbN+u@8_wBkX>2(bK(Q2zk%P7Q#@ZY>no9?l zAYa+Gb6{r~WK({Y|6R6q8N{<>^QB;hIa@8UTyMkbookEo`lV;x-xsszpxAxRvr( zYXtXuzWUwnoiDt@z5Cz)YWKePzSq6`-S2kyAiMh4@c7t|{D}L{{?6Yaux@hdIS^}r zBafgAE_|LbcnvJJ^XrrgZ|aX;_yW^9R(o#UOw0u9%gpl`B+}e#Q2JQB|62d5nVvww zY6C?%?u0Zrq&cg6_wbp7yzWF3Y{?mt2q49nqX8V1=J4!xdzTnnUH}VYl^TmF47x5H z`osn}ScfW*Yv2f2#k{_D$)}CoWpV#Lo2y5c1+QjQo>BeZ*o9=oxnjLPUJt$`dr}#$ z`rtR)J#Tk-*$}USvR(!O>nuMjz`d$fb{A#A1AWHY-&oLrve4Jx73$S0`LoRJmnT0$5vQ$n$`i=b5pzTQ@>>Zh(w7= zzsSbnpt7lCJE|-^14ZmT(h0Dha^sR=+2-UE?N-t+7+^gN%IcA}Y3`heJ@;K=#Q1ha z`?q(_b>5ZWu0T+3OiDKFZ@kXQb;hXE&FG=;(^z1vX|w}0iUBy|JLVo{r;&Wk*r*1e z%g&qHVI%D_NFWG&FgB#rp{Wz=75?S#Rsw2{!bp{f8GSCwq#!;3QO)$Nr4r0^&74X5=Ulp#NAKdohMBRdVY0qd%jdr}@b+MqradH95` zR>DB9_R;<)DjmJM()p*MJTv-(b0@rCt#vV*QLeSaIXfN3BqzD$$5l?DfGkM+uzkeq zn)L~D0}tIacLh!TGW4AWX^Lz+`j1FK2LhWRMYlMS_lFJlm+Z@|Qr+_3?BDf3`-uGn zlttOP%ljIDQK$UmmfyAZ!uqf4R@Vg@gc;ku)7;=KWNw4xLZwa6Htl8_n<2Bif256# zetf3m$4=*lMgXy1KqH&SIaMgw=vX%={XEZM>g7EJ^#;I1Dd?)v@o#kd-2;}Pjn6bY z5R@F{eTpDoY-6@(v*9?y;8qP18b-?{Kc=+>C8Xo;UBA`W9vh2veno(fGBsE2ndH75 zHIM*htXAyP088m3>{}+WEF-_AB#?JI0qtSy)k;7&8INWDVD4FOF05$+lqKoR>$pd; zQfcRr{ZyoKow5=E{YviBQCYs6KOn0O@C{{+&ujcf<#;RJV_Ien3}zs6l1y3+4j#4& z{-QM+X>a7bW~XUV`JFN;VQf$iOPR4Hlzkh~w-Pnp$~f}{=P*`0`Z1LTApwt$2KgS=qF(QkFR zp!s_TX^TMMd?x^StWQ5kaAa5Q|X^5yyj=)sG#nuNd{Cx?T?DQn*8|j{Dou&Ne(UckNoIt>r^4OK-_l6Zt zC2(Vo!FC&VterQP^Rm20$=u8DGsi$HH~i;VZF7K63BVkyNB*~!qx{V7o3qsB?z;q* zl)%lLZI+myb3jPWpv&2qcG!2vfVL9wECKwv-?=^KKAW>cd3{-?l9fDnpAxVsfwN;^ zV_DXlfYK5an)AcCXO!j0uR9hvP}*q;z_jm=f!?=0<^bKL=g(bt6Ihztt7HO~!6Qq3 zVX1sK*<$gW-;~&5FnlnK(r#3Qxh5(0iDTgd=%y;B4<(2LF38>T`w8y7D{jc1oKJ_X0 ziC_N>_hTRX7#rZl_rBMC{nve+d+}?(mH_czm*a)6`YQL4=x6`K5C8BaSC~LrJv2B0 z$EX|Z2HfWFY;9uR$tG4}5c^4f)j$~L*aA4$)G1G|(KoIAhHlKphQl@0K_Bx908=ut zNF*MKuO+V0039Jv_7ku2e~G*QNRtaIGt>4 z4}Bm*Lgenp6xbpGEy79`(5cSAUftZ}=4IOMOhpUD`ukv=i?NC2mK=FG`tIXGN z8r#h#Y0arp40(R_FZl!QIH)g<+O`V#R>~ShJs}f|wn45*X#i;}31H|+a>{+5$`2+J zYQSp{bq22>d+H_%0QXlH!sfQ%hT0@0o!UKK-)K9J!bb8s7fk?wZ2l4)0x(g?{lZ_s z_9LJjq|Jj$&DO1gV!C;0;~oGsFu~|6jm%$U=z*E2;2P^OtW+jiS=HK?3CtM)tYa%! zg?UbUmx3Z>jwjHC_Gs$#8T&S*d{r77b2nG*+e&^-Q~hB7Kam`c%#V%D zTQsqP=)P|!U=ED(^@@45(Z9)L2J26AP>-rez*Yv+8F^m4f_Szl=p~SWAOsn}gX=G@ zB)A}+kNPuj^b>M$ZM4?>@&NhBpll>iAdaWi$%Y_0Sh|@OJ3leNB=`>lkD zC;=)xnZ+TjvhliN4r1?CqOJ&bHTI0|oK7G!#~)80Mfn@DgJa$9?-?+GzA@~FXuC@R zz`=?ITh~$*7+42(8R~Izhe38w4hYKHHPKm<W{ssdpHZ0F$r3nkNm!|JOqqQdYlaChXj8&r}q+rmFpjsSQc>l zg87)M=1lw=`LG*NPLkjTliijTc1xhFcH8hdXW$ROU&^t=$7{(^i*xX7Fefoy!ERt) zw)F|Ytlj=0Z7X^vgL;Q!f0vx-0$2fPMV?sX&uv%d3MNl`_dP(RP-*Vyn(c21_(f(<->0Qo#Vm zqsOs4t(X4NRXWyr@wHx6aD)N8BPfr$4D<~IR_x)S%=e4ykc&5tG;Dig(BnEXvD>@S zeh8$ny$1s-$3B6o{I(`_}|`8D{tX>G3{D0NJSh(Av==+W_qDdGD4CVv)nH*&eo6B0+`| zC_Bkwi@f#dhU{}=qOYkc){j>*X8XYU<2htZtNX%NFZ(wHtR+ide9IJAbhm`%_PPc% zFuvCD1^yqHhkK=U>{$=|-IN>na}1~)%*wyZ!JKjiE9FT`>EN8Da}&r_+M@&v%ks~G7dZfw+iHmcHwUa8D^q@5{%ot~ zoGs_tuPj?x-qQZ1Pn15h1n$~%a{%%t+3z`9=l3uDcMdEn{W`yA?jyPV%KI6hZ*{0u zvTaV*c@Cs6{dcZDx!>gX&f8(m|I2&Mfr)v0<^5pp`rJq7#*flR>^^foyLA0Akhf&e z&Xvu|Tn2#5o!?}eY;$9%mBT)t8>4bNmH_gSpXKeB`$Op)x&89`m-agbn$I0`DFBxG zMm~Pdf!n1D!(7|UjmhPACHSBFL_U5mjq!8icYbaDT^;~cw&C3PS^8m_HXy(DSAX?a z-P7Z(apYr?SMW`u4RyoaHaP5DH{XBef)&!=x)_$1&+&1J_w;yzjvxM^|IvNJH-3|o zHhuBgE;;Ri$~F z0$)uNTiAdQEn`zjt{jnr)Ts4xjvX~WPE${L_;6Cu*{kQJF`uhJe{`;-p1|Rs2?{L5IFaCx5^G)|yIb%r@I;Jk!0Qmm8-2p=G}Rpo<&wLHXuVdIM-z2 z*)vCxkgt{bRU2R@9X12oCPe^YCRQrlEMOHtL;%zMUI0M!Q32INLKEi#l;c1|FxdE*tuYDw`DvbJe;{i)Q&MO& z2l=4V_q9ptb`zLs8&Cep0Dxgz1KB9|Z}O-?*p~qT0)ymfYgV$JVE}_F2mn07ni-q* zVmF^Q{4Josm>iJZrLR;PxX@2|9tV|oUY(zbulM$jCrn;S(5_l-hl(3~WPlKW)EuPo zy8TWzWx@6%^P`fv(?iM?^3u-I8DP#BCI2a|SKsv7?#PE}5{i^!CmXDhxn|e9(M_F? z3^yHMoA_@sZc+YPWM$b`ImgR<=e5s(-9BnyRSHx9FbN=B`(=B6LVqWK7j!Nh<^8By zH;Iv%hL1`z)5+W|Y()4c`g*kzb%Al$bA!G`o>u1JjqSc4$l~rEU5bqqU>_~m0$qLG zXuyXjVAs%gZS;#K3846HpEkwW-WEWBqt)Dix*jHTAJ1P2I}Y`NtN@TeZ@;sEw5v3> zffe3Ax=@xVn4oL63EAbg4kAG1T=(D+$AktF-pcs)J1>1y$G~wfIb8j!bx+E{F>Ed6 z9WV9-d6{!l0|~%xcF|7=3gYNhdb3fm5#yh-@rSEDfiw6df?&gTT3fc~XX5|0fWE=f zZvZUb2=E3;Xby1-@Jg_S?%!y88{6LvUdB!2d%fDRJyy>?uK^9cO0GJWL=$YD!t=pS zt;HL;XWvxT$utmQ&vvf>xgJJ-Z}w(HaAf0x26R(5-HdWZI?TLGE5wubaH0w^{o+4@)0n*pp|K?)b_NJ#M3 zlImcbKnf}qV62JdK)JT}|IDR4shZ21Dh14|+RC`)g-J|EV=wZ`PIKoX_=XbBkbYln z2wwL4i^NEE3OZR-RATOnw1R8aXMA42zl^>jfOf6(-6)Yht^>#cP|>T(hp*Ebq{ldb zxjFW+eRmn>7y*W5&e_v%n$?D7==;k!r=E}<2=H0vgjUr9I?7qsD;az7bbz`XlZHwe zh+YDC0&{Rkf8jev3qa)n5W{hH=PP+{fV`GE2eRDoF8Bz_dDd(=#{>YYm89NiYhhqI z|496*l5q<_WYezY9I%Qx-}Tyl)4Lej*n?@>iR^4s8|dc6hF<3^Zva*`?LFvv3;zc2 z<`lG(x?M`WE6Fd5zTCK#VvJJ&Cz+ED4A^r%r}3gvL22F~3JPs|%Ch60BY{Ynhf(+K z{=WEsb;7!@S9g^eDs^N3uZ*SJYy3w|kP?7c-O9OV_48@{N|1BRKw?*=_qq-sYq%g- z+&1z|`W+;f!Dy~GZ1SL>8l;KWo4Yz*>U&JEAnS^f&3MGNd#KD!O;dz7Zx;C@JXpA96hRLRA=DF~a&0nYJ0 zCaX?%wUZzVN@174j16Yj76ip-H-Sw{ti2K(IR+LS`};PZ-G()}bbbkf&DH(3&v=~U zrstJF;!V$=tHUw2=i?m5SZ%j?#^aRZaqK)74Dc7J*Ag@Ew&l9E>3;*zUfSe~?ORLb zp6io&S#Hxu=j?sV{*TMzoI7Xf`nmEhjTPm&OXuA**38LL&w=zy?QolMXYSq>6p{x% z%(cTYyUyA0w$J~I_OYe9KF+u?SKc{*zP#qx{I(RtQa-P==S^em(wH=NJ^uc-Z~Hd) z&Ue1^FJ#xJ$Cu`DZM~CxE=!T|nD?9wl5&5!bH~%;%{{6Tzr?EbZt#% zff!^JDrjznlMRimU*>(y3#j!i-~7$){onjeZ=N&!6&=6uOTXy8;r;J--|(&PcR%yb z{;9iv|Gu`JS2=q#yTV{gb9)fW%05V#d_w_bGk${4#gjzK|A9G`jhGw#3t!5?)0 z(R;tvefLW*x!-x^l{ec|f9c0h{^*aoyLavgINNJ3Y?HJsg4(Jgz%1_-xliXdotV7g zpkIv|oN6a61ru98qP%(*Aa@XuY|l{XS$T%$SJZ;~$~L2`gW$!3esA)~76=VMSjU%9 zC0#AAZVHO>3N~kfoq6uvgSrI?_AoXzB~2Tt7xNu!F7muwCXXu{sndo`ZhWhk^F9H? z&Nk%gD&LK~|Xpk5J-`=x$JAZVS~+F~XNW}+{E!PiZGA@cDSXi{sA_bg|- z0T&4bw-f>b?2xruW&I#)e}a1}W3<|r8hC;>Xq26+;3@zoN+4I#-@w#0FvGy4!#PGb zCjG?Z_6mp?n-@%FF!oGx)xh7%e87S<20^Z*Eb1fH$E=Jh3vT_wHbezU8Ly4YQ@&wR zAt`&yO~hf6B9~#>PqMi-FXsk@8#7PtO6A=J6r!|hugqe|??O6QrG-%*u-k-Ttl+7e zrfDP|bE`+cpIH z000x@x1e2pzRx7)1>D>c%xAq|yS3bdz@&|dgRU={CnHEhJC=gI^)#nDu_r)MvI&u6 z-F(yn!P|NYM$vrTVL#~HG1Axjt1Drg1IXO0MQS-4>k=r7e$MLwAor86RoPfuSBEM} zww6FUnMxXRiUV*>eQrmIzFs>2Msn>cH^8;-oY+uZm-PHryW$Mq96uzsCaX2ZofZa#=WR>K%`4@j%zFh&N zC6DQ->}ZiDb$m9!aI%tdO)G#n8LF`y{eCNXG9k&#@p0?A?IZpUDZ+IM))YWT*t$V; z!rGb<;1~cMQHKaB<10)?=Di*JY|SIm%M~Q0eWd|B@jR3Za?7JJ1ce!!tC&bGTS+@1 z_ZE5T)#e#W%nz)qd`=(}a?hC0;`5mImon7w^HCLa8p-XD;Ta&p%(JsP}UnpQ6!Z|Wj_YZx2kPm!ve}`Ae7E~ zqw53^^AeCzRjMcehADwcWu&9M!k(Zoe$#(^qy97Kuu~JXlrp#ksG9t?%}3r(=?$E+ zEs-Y{jAxUwW**=1ioiCXYd~TA4YsQ+$%JWI)fO1!B@ZuHlkr~U43AgI*xr}xY+-X^ zE(fp-@Q^aWK>$2sI|EpSjYO{ADZn`@OS7g;+1_kFVXe9>QIiD}IQ3IJi?$^TAHbym z$s+k}iNz~EQXcJL$qG|4BXz9+Ud#Ci4UdxLH&@O+EncT+*c3zn;c8RT9+Asep zS(c?em)NdlTl9T@;6Dah-Uj%b12ReNdxO zEq!gy?qwU^hMiv4e@=FPuJ4r3FYPf`wqu}rY46#Jim;Y9POC)(il12>~12g%W=Av^; z{lYoT4H4JD<*7nlsAd+FH*mgC6%9h3%4FifhT`Ga7^0=BN1Y@B8lWezRTSFZK9^ zfBsAE$A0{ObFaSk8W|cvHzjS{G6~z}+FT}J85xiY^p55~i0ZGWe6oD?w{=66z1t{* z-5cF&6qcHA+jOnvv&FwZ{!72;J|4^a-mm!@_wt8cV$R^V;sGh>zyJ6Cp8K(%{7D6D zyz9D7m>6W@^b`BtY@{rmU;DFcdQ|;T%CHAj9*MMWlz@~m|fP`)^_RPUKX27B5AZ#Sh@Sto@jwz-v z07hHoQ;sXmFB?6Kw&It&iW~8iEB15IzY9=BGyGHp$*LCUYaqjtTIs zWV2Lljt%?%@*=U5R%a&!I{K@aMd_^iu1WJu~|_MFkg*}*!D`v@4~ulqF(?o9SIgo;7#u?#h2H%xf?WK0{umDU{a=5C8)u^ zM!%{Xk>?Zuv~>-bnAqe+?ohDr*Bh0?Mqib55Ck160bRjZBv{={tX2TI@L2-%U^z>P zQokqA(#f3BktIB^Zza$iV@apXVF0Nq&;hL6oeQzg^^^r}Tzk6V7*Q#h-glbUvS|en zr@XfSR1R{nTt7e|U>83hnFo;ssM^` z3gNR;o&%X-Z(zU*eKZTm(fpRuwok@^E$rQchvHkK24!4avA*rSyKW#r-pl4WmU_zk z-p9|y|3f_QjsS-zD;M@)`E|n5jP192_8#qc@&A5?Am-}s9oOEy>(+03KH7RK4F6t& zfsngbfd1HD9zAly!$SQSoEI-Dd(|5eU zKHXnDq?(M!G*ah#tWH2<{PernuBWa(*~qxUJquwjlWhs$6+mrm(xYBwc_9%U#{tM@ zS5n6A(fv5aY?HD>dw!44+dudMeUx(Voy-T_22w7=P~ol{VXt#3&H;N|<{I3KSysUgCil@)X(W&l3~D^Tj`yIt0OX9vAqAI`$%-Jo zW)q*kArpD%qaEXqopn1gYOC6 zk@4E^6)>)q`L`2A1}ZHzi8U`Wy@4{vD1Y0WvVKh+pVvJgTh-D8Ic+Ndv$z-8x|rkG zzdDYKRn@98)IPlfru{^Tw-IKuL1P3J_wv4p_rKv0XPBD#W#!z<{&itMQtHPw8Zql4VDmGoLQzzp-%!U`xJoVIjkB zR<#CY1O-4Hn5riP(?tS2+O*O@ld(%+zS(RAR%~$knE>7dC~=>752z-u*z$Kv{fY%&+z44|A_Xi3>>XQF-3yc2L>h}n4nh1 zJM6NlqW=$Cz3-KAgv7Wvi=divIUL{a1G<`#z!e&zE;p1%-=gF zr3PTecJ7)bP_``7ZCPha{b9}q$NKy+Ta@=+Vq`AaY^k2do;?Rd9}60oJ12j} z63D*vvt*Ck`uI}aZ)=;|TzAv+Z&Q!I$k}LmzUVdF{0n zIAD_A+-OLw5(Uc?oSFDx`CJ+f7}R;&EUu$ckc?Bw@`+I=ch~%~ev+TYb)S6Y_sCZM zo|j&dvcCn7kACoj%suTVGi$rdub&QpRQqw#&y(zSc36MQ$C0Tu44Z13yC1bLcH>CM8 z=+AW0RIY>mn^!2au?=Wep3-*4J_dMa41O!OF~*H$m|yG7$1?V=?h1=9h3Q8ajP*gk zWV!X3Q}D0G!_|jlmN2!|071a>C#io*$<>aD;BxThp@BR;<+?WJT|UpdVQQ1$a)!6J zK~!w7EK+{fWbAhF1ZO1nbS)nvRv<9}xxKrt8S)-*n zp;9-TzL&nAovQ&-hpaR$gUzQ3bTT+WZNW0Bk2&o_0*X!yXHmxpE?)ylZf!s6nT`kk)iovFx{kJgjTFoyZ{DI9@3`Ggr8pg3Ge~IWIt| z{hi)v>ozfuE$698RI|2WoXTN?O)z`sKkTL229>f=HNAq}-|Jc^PxDm?3)ZDOg>1|l zr+WFkm8YXJKu*h9C8oNRDFuh{dDhPibo9u5!FkX0**t1>O=Fybxn>)G zZ9tmVQZ57Nr*cicJSjkg^ciy-hf#sbknF<1C}n_;sV~+Ob5jOy%}JdmSk_Gd!)Ef4 z!p3F?1FH3nK<(*Mo8|L8eJiVbT1F-L>nWwCNZF6m^1;RiZ-3PauIbxhNX5~zPN{Be zjC6Tv{M4oi+4R}k)I!_5tuLp)QyXOWwtBm1(5mzK%N<%slO3WMg?yPnwnsQ($}6i-NX=Pd(Mjo<@$uW|wm5d)L8x^z;rr{XMDL zNZw}k^Quy*?Yu|i$#IG?{@NkWYL5p=|NJw5gst@eE6|WIP@N z{>mJpc1{jTm-%_i+@ksOb4EicwLJIyn?Q)%b~zK%e$SQv82DN~)7X`EkCOSBGuldK z@6s^`=;O28Hdeke2WH-WokpwW*W|Xy?Ooby$+k4$S|kHc7vd0mfgSY~x9&&_R<*Dd$y+{euxOV^inF9QHdpR6_N_9n1CKT022@~^pe zDcd$5fAZ_*`n1*C`iOmI3I@2QX`0!-p0~G^D`%a~fxFBz23fI;L7MlG(!O&!VavA9 z{jUrP$m=`D@-F{2e_;fE!E(;acWkUH#}TXBoNt%of7zz_J@Z`V`B*x!-Z9*@N|7oM! zzBEZhH}%J{@ICl18KO^*x8(6%-}wReGe7gQ?$uXc)$!7Bx@oi3j&f}{jFaS%z1VP$ zh+`XAP4KTy*?mn=wF1lC8L#biqpcjb?ce#_nvb2yKQ$lkn)%g&Fc=F}1lfG_%wbl} z`@QDyY;@yb@WVA_ku-kmW~0>Ae#`jX_-a~@V*ZBB8eu(|QUeZK!-XIlabNXSU*%r< z;0NDq7xqz`%sC@yv1kqaaD0F?AFy zIZzkTb6{_@E7-w!PMR$(2e3xY2W9%n=DS4x`@~KjMWQ#}_v8QkSKKfD@~^m;Kl~y0 zkq>^~n{C=J`tegg{^RcNe*3peKeY8{zK+q~$a#y6_3e?6+I#Q!1ru>=$c4SrbVlIzcpCKj!aox>g#6O++T^YNPqf@5|%8uxE!bb58>MOa%jR8iMaZt80 z9p7<-exQ%mWFk`HbQIgxjjo9`=WLSS-gmO$iMlCE8u>)Mv5}eE)=wZ=CI4b?V|RX6J-=iG0vcs{-d zD|&UhiJ$FV-|b?X?gSiEe?=})GGFQ@$t?@08QWPI6ecwcmREhUcC|80@m;f#T)y0x z?BypY!Ksbsa^BePQ-5mKCmbKU%S-0g9W_5C*dk!OBJb>gJiBB;3uCv5_Zhmubr)Bz zy>m)AVW<8FHZ2a=p^tvv?>g76#eb0JmMKf)^Ck8oP+Hb8PqO9QtoR%-kI8(+zb%L2 zW+Qt$yDkBK%!f(|XW8%_1_63R5_w2@zWLpDPbM!Il^#s_l}J8AL8Bh0b*TX-%&UyH zgx`*}Fd1<#kH$epxb}TgeN^bqiVkZS+ZLQ{;d}TdTPf-02ug$>56ZTAiK{U+clZ51K7Ig z|4o}DkB9Ar3y^DW&d8ico>z=PV6L7#^A*#W2!(+CmVIK3r$6Ge)jB_acD?VfU;InZoeTMAWnljR{RdyGYI^93|!nBt%NIuicFMLJ-)y-KPlj0BNb;zRj zkAzJekAC}58Sr4>p7N*bu2{!*bC)dO{WjXIZkdCWtUAbHZ%%b0a1w1Yh6Ln~UL;(* zQ2-;fEm_a8+^_-j)NTdf1xuJ<-XLJA$mS|@uT`*hVk>kB~!+pt%U5V<-roI>7?Y6?`uEg)#P^EPj^N+f{Y!M^oCEd5A~mttF5a?V>2DPG7M zs}(4P90X9L|1f}{Vfjp=m*pn=wPs(bgV)@x(Qo2dfX~6W2FML|b)^j9N~OA+y3=Qg zT=FjZG|Jn=cHL|FYn3oYo>$n@^7e8R4x%oD_H?+eUQuQo|N)D^}N@94}S*pcU33CxG%`Q7K+7Vt9m}E8 z_rk(u?PRSHB!CR65G|A%@XS#Ih_E(|;~}2yR5t&-{W8||a@=YFMVxmi2@R(3hWFk< z^}tCGS{>p%3?FpDG6&N+f!lnCX*f2ERJb$V^8BXx;~XV8W}h8fU@YBd3E*40_c1{2 z*t6$A@AC7TfbpBkR@S2glpd$tOSW19AfwzY*4|5PdTd-@ zI_EavxBY%x8_(Ig1T^e;D*gP{JbvL9eu2M3SeUnN@Z(TKho->MgTAAE(M{j|{LlaV zn_cy%$6w;{kN@#MO8;J(Bpjd+{yD2L9T=@i*Li|K{J6`p&Fp@_+lv0NlehKS4?L>C}(H zbRDJRAd%WwFkBH>Xt_I0$~R;E88~}T4uUX^GP z3D|QrS7>5zd7qey{#qFns8tDHVKWP3( z3-q#fn|Vh|bGXw{7YqMLrcy$K+aq7y!_jtbV1E&<0}R zx%Ijsm^zKg`hV6hZOt&qy(rhMfeFmVYQFBAs9UaNjF146n)zE((3O?ff~fjI>t73? zrM6G&1%Pyr%dKk#*BZ(1o5{1wm{y5wGy-5y7XXmW#O_7eN1NN#CT#D@YrpAJngTaU zP?&*^$ki%2Xlu%WGmtx$iNOS`)j{1D$bzQogXBAgoe)rhfCT{Ey^S?bMh;+UQwrUt z^%mDrW_QhPOF-{jgB5!1FJ2g*=s(P@3@LE>GVFIEZRrl#-3-z|Km+b+tYS!&?{+RA zzcTEuQsHzq^NXrhu+VZ&w*+6M?L{6R(#ez}t|a%V1QK=1tgRUwgt1fDr8cJalwh|0 zi=Yws06=Sw^^Hn!4!!0{b;<+`@;CmCAOV7&t!qxsDWPori@}NOQ-03zjB?L=4W6rL zU&vN-g-YrB2eF)6xi<`Efp$U4AbS!uWs+OSIImCm!?}gQR%;o9Yz=_^1;8-=hIBC7 zCSD79>-OZX$V4}iQI;Rob51_-e_QFR0_OkM=_RVMd7 zh*W#CO798)L49NANqYhiL7w4$cNzb^bArA|fLBcxZ_C`gCL;}Stw+vaXXA60k6Hu6 zc+URP4W0f#iW%3FiPtFTIk6*Uy=il>jgyYgudCKgtWA-k6`*BOq_KHg<~s2yVM$9! zcwLJuu5_J7L0iXjy)$dIm@Fse%C{jVU=C#I|yd<+)A0L1GzPGnLC{?aA^s$}%0ZAASFJDu=BA zwAqv(Z3r63W9kp`T|*zSJhwgVDeKux4M_V>8W%DK{`7y->;aIocbfay_0Hbxr3{jD zdF3~3OH&J|H5YPvRml9jvK-mQvUk(`hc+IN*ILW&+@Qe-b;cSV1z62;LKD2kn3djR z1N{Slz4&II{2%s{e7`Hrk=#vf!g6Q!(EcK0U#B+hUG^e1ovD5!Hm-Obq3O9z=yYiT zGR$po=lNmnoSX?T$3!!>Fz2#@-DWSR`KCB{fQdRpDUgqUMTm(0eSjuK3svul}ec7RD@ zp1J3~>pQ=TvcXS}x7P8#7hiPmd*6%hlfVD_(->6DF$Q6T;c$*OaF?4h-q?IjqnMrU z?Alls*L1yre20Q$a^!P$t`Z`*zdrsYd1W`vbq1p3$}Kns%PES_jCrUVuVCpN?98}n z{bDTPTIC#Vy<_(#+jpGi>1$&<%7Pbf&G$PfqwpPH^$z#`Z~mq?yXn7Z$NdKn+;@NQ zgYMUV<2PhY(a6S`4J*@&vR8&_h%fWvKiKT_3J#N%ldls^qO;0_46+~f12Q7P;z8SJ zyY+pae29!yErUseRSAq9le|`?_X!Fx8CZ!i^^fBC`yameweI^r^pgAN_kHNiHu)Fr z_{jHskNc*7`7gDhESLNg$dEuEZhRaDM{o_7D;Mdtlr){R-dww0vk!tTTkD35$y^SW zUl@5iEf=pd@K)E~x{kB<;rErFL=SbmngTX-(^PZ(Hnu@lPn(YL50t;%taT%N5H@aU zATJD2XJD+#!~y)VJjfg}2a|l)b?D#{cvJ;tAY&{c$fS9exxsEATg~UdSGf`86?_6a zSphGw&e9mC?94%dHDj$)vQs~A3`DEF_K`Z(&&uCp;}P%MyUsm1hxXfO8{}@~*ip$w zEde}5QBBehzW~wzI2`LK7|wuvu;9AOE!(D9x3LXHO#nGP6EK?jgXNOv#%VWD<)DF& zcft<#Dl1DVS}!at{^pn{3_yStxEFKNa&GJdcqSbK^{LY{aBu5x0Dl2GfmK`)43qxj zH19QYZz7lOR_en#c0qs}fUE#-0dB7aG#v&FFu-^R7CrKOgNX`8Jn|%~|C4zSPyu~0 z`V(^K0>JGrwzTC~=~yw=NydHD;0ESE2ABpL0SMde1fWIvz~ls@_hg;GU#CFP$UN7Q zlUaZOM?ea5cD>oiI77h2Nq^~N{o9;v2ncd5OeR?+xrf4 zv)Bq^TPTW0dip@j^`Db zuP7(-={k0bYHVWfkR%fP4D|2S{d%>T4_aYG*f^Hae8HQRu>ZJ7vO09oek^g$(48@Io@PqwjFb}Pq`SeDi4v-)0B z@jfFNii0eq>MSVBx(Aq}RZArW!lg~COu(Q4NW0^ANDLS$hTx`yKxG-L3?XK1Qb#^9InR{fJM%CET4=k&rPBs(zHkNy&T zx)y9vurZli*pJ$_D*=|qy|PHx1h7ovm_Smyk@g?AZq=OgoKb_kdaybP+)I92JcIF6 z3dqaaD%wI>svM8;9-u7sefLQFd1Lsau5AOErDQ}lXYsx}w~y_Awtk*vxY~YR0Dq^! zP@@3fBY`^&B&lU>2r#blbpkyxH?_4e*a6THv{PoVyo2o7);K4SiN2*BP4ax?JJO2q zJpgY6U|}8Gjmi%75G4-MO9 z&s@eWf%rxN%&Y!B?F7J{@5AxDItXZtvH}FOwfU$)EGRiMMzd{J8tReB#|MS(E3s4K zHfp0mSi$l0ZhS;Qnjj+!EMT$Z9^Oe=!=%Avvv8$C7Sf7evs5dlHx-kQwe79 z@mV`QAFE&12eRBMgWvms_daRv(w<$|Y0yT?dr82dLfKFsV4K7>qyp|~|5$?3F%nANMtN`cw1B{m75}i2KnW{fDv`PtlCVI$93; z8ZC0wSb2HCNnw(F94PHPC)uB>pa5gOOKZ{A_R$M3yud$@^AaF7WYXXFpS;h#^M!Z1 z_q^vle>G0|C49X6@<-i&{dd2S3wmpp+D~lF!!_O%NI==Dw3qG~bL@&>uylm%-0Hg0 z#hhe+x^yN`>L8yMHhJ~MS>8m8b&$$Mdv%HfA2a4*F()1Ppic#n*Ei!HKe~LX>SpPc%0H-qY^U89OXT$(2oG|l@^)a!j zXkTqpfHoY@>x@Y{sNGk(DLe{C%Dck=9)7~};~2A7x!L+iKO4 zo$0h)XcL@cfLoZ@oW8i@+Z8j$oywmNb;L2_{1?B4c z&?)18++8sc#QHnyDs*8{JV1OYSQd9MK|7AOTE7~>j% zZAd^nm4fD+&uf1Veco zAywZQ)Z&jIc8UbfK5)<9{@d@4X=aLwH3{tPkCej ze#51F8YI`WjQ2h<7H#dFa_d%v>GXb6+81ZcL9`nH)OcT0V@ND$_>=_;>wUdSX4h5g z%A+A;w<=`4o12DWY zVF_c#bI1Y(NMCLESvOorJrIa63Zu0pkZ-=J+A|6$TCavQa8Qt%fryke-e|qYssAH2 zgOp0!txEewL45>}x%s}povi@(ltOO!UWcHB(f3oCz(949E(M?@8S-AhVU)=NBaF%H z)JIlm8%mjXv|UXAdCOEBE67D_;DEh_j>^C`>E}_&5oRA@6pK#|n&v{7%8sBY{g(iI zo664V1MN`h`cFBs!Y+4-T~6f!_yN9i!DT&%HW~H5Ms2iGdGm>#E#8azOB~b4HnPn> z)@`^-%y~`l<}47O^M-DmrQrBcWWp;|Ah3PUG3;a=t^P3q+S105%4R>zb2v`(|8<}! z|2_BbB@nbce+d*Szsp}Z=h%VeH6^QViv0K20Go0%>KN!a_sk`rWvTpg`8kit*X6m) zN*m7s)pK>6JIeCUu_fo)cMdRI`mAIr&iO&fHZ9wtWLMg;ly`Hf9CPR7_Ai0dIS^+K z@Vkv)1DV3!z(OF3OjpsK9fF$SX@)_-yacjo zV@k$r85~fyae1GmBlo%7H*Hg=WKJvr<~NNS$Lc!g8>Ro0_bfs6rTpxr|13RsE|>D` zruxFY^Uvp)*Gqm>0{eMc^7fe<_m|pu4!AGD{qo$Uc3T?b^M5}MsGQGjINtTHzrO5W zfV_}M{=_FfLEsmEANbEON$aIKGKk&k>Usd%6uCEjKIcE&I4Z`E(%0?WrND#A89T(K zXHMgXUY~DDa)s$tCCcZ8Jg`Y#2EJs3i7ljWZV3$I^Q!4ylMgz+h^7}go#%q(fAtfP zZ1u{4yejLX@}d*6vK|KDwO3!`{r>3R{@Zlz&;Hy$ z;dyuO+#v{zyxA|l_eFxkf8CB3-}_$o;)^f3S3dd4^b$0^nq*^bdP$o0GpvOz&n352 zi?wDB_&9&KZktnHm|LTi-=&QZ4KkR50Q4C?8$*q$rxKJ0n+!ASNAts!pV)xRBqy8o zuRORSOpN+`O*lF$i+At+ny+zR8-Lyk$L~ge`Ht`W0E22I5;_|@igT+<<-pa(y)ia* zQnnro{hr_MyG$Pte&yJa&S&z9@z-Zf@Fejcu7vypt6p^ zef*by*}d}0@4Nr∓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�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

o3g%?C8RPQV-V(8eLDVn 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 z.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 literal 0 HcmV?d00001 From df3b5cead886f06d44f058f9190c863e36c1b73a Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 25 Sep 2025 16:29:05 -0400 Subject: [PATCH 045/135] Readme --- README.md | 2 +- orbit_sm.png => orbit_shorter.png | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename orbit_sm.png => orbit_shorter.png (100%) diff --git a/README.md b/README.md index 888c663..64a4eff 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

    - Orbit + Orbit

    Orbit.java is a set of independent utilities or extensions around the [JNATS](https://github.com/nats-io/nats.java) ecosystem that aims to diff --git a/orbit_sm.png b/orbit_shorter.png similarity index 100% rename from orbit_sm.png rename to orbit_shorter.png From 571cc744a9d4d995c7b670e0ee8aaa79f1aa8a37 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 26 Sep 2025 13:06:26 -0400 Subject: [PATCH 046/135] update readmes --- README.md | 32 ++++++++++++++++++++++++++++++-- batch-publish/README.md | 8 ++++++-- batch-publish/build.gradle | 2 +- counter/README.md | 6 ++++-- counter/build.gradle | 2 +- schedule-message/README.md | 9 +++++++-- 6 files changed, 49 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 64a4eff..286899c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Extension for retrying anything. ## JS Publish Extensions -Extensions around Jetstream Publishing +General extensions for Jetstream Publishing [![README](https://img.shields.io/badge/README-blue?style=flat&link=js-publish-extensions/README.md)](js-publish-extensions/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:jnats--js--publish--extensions-00BC8E?labelColor=grey&style=flat) @@ -60,10 +60,38 @@ The functionality is described in [ADR-31](https://github.com/nats-io/nats-archi Run some NATS servers and cause chaos by bringing them up and down. [![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](chaos-runner/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:direct--batch-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner) +### Batch Publish + +Utility to publish an atomic batch, a group of up to 1000 messages + +[![README](https://img.shields.io/badge/README-blue?style=flat&link=batch-publish/README.md)](batch-publish/README.md) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-00BC8E?labelColor=grey&style=flat) +[![javadoc](https://javadoc.io/badge2/io.synadia/batch-publish/javadoc.svg)](https://javadoc.io/doc/io.synadia/batch-publish) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish) + +### JetStream Distributed Counters CRDT + +Utility to take advantage of the distributed counter functionality. + +[![README](https://img.shields.io/badge/README-blue?style=flat&link=counter/README.md)](counter/README.md) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counter-00BC8E?labelColor=grey&style=flat) +[![javadoc](https://javadoc.io/badge2/io.synadia/counter/javadoc.svg)](https://javadoc.io/doc/io.synadia/counter) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter) + +### JetStream Scheduled Message + +Utility to leverage the ability to schedule a message to be published at a later time. +Eventually the ability to schedule a message to publish based on a cron or schedule. + +[![README](https://img.shields.io/badge/README-blue?style=flat&link=scheduled-message/README.md)](scheduled-message/README.md) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) +[![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) + # Dependencies ### Gradle diff --git a/batch-publish/README.md b/batch-publish/README.md index 67ec123..c62a804 100644 --- a/batch-publish/README.md +++ b/batch-publish/README.md @@ -2,8 +2,12 @@ # Batch Publish -**Current Release**: 0.0.0 - **Current Snapshot**: 0.0.1-SNAPSHOT +Utility to publish an atomic batch, a group of up to 1000 messages + +https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-50.md + +**Current Release**: 0.2.0 + **Current Snapshot**: 0.2.1-SNAPSHOT   **Gradle and Maven** `io.synadia:batch-publish` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index cd6f795..245d0a4 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.1.3" +def jarVersion = "0.2.0" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/counter/README.md b/counter/README.md index f7dd5e1..567cbc0 100644 --- a/counter/README.md +++ b/counter/README.md @@ -2,11 +2,13 @@ # JetStream Distributed Counters CRDT +Utility to take advantage of the distributed counter functionality. + https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md -**Current Release**: N/A -  **Current Snapshot**: 0.1.0-SNAPSHOT +**Current Release**: 0.1.0 +  **Current Snapshot**: 0.1.1-SNAPSHOT   **Gradle and Maven** `io.synadia:counter` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/counter/build.gradle b/counter/build.gradle index 3539b52..ed6381f 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.1" +def jarVersion = "0.1.0" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/schedule-message/README.md b/schedule-message/README.md index b132ef3..3f36760 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -2,8 +2,13 @@ # JetStream Scheduled Message -**Current Release**: N/A -  **Current Snapshot**: 0.1.0-SNAPSHOT +Utility to leverage the ability to schedule a message to be published at a later time. +Eventually the ability to schedule a message to publish based on a cron or schedule. + +https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md + +**Current Release**: 0.1.0 +  **Current Snapshot**: 0.1.1-SNAPSHOT   **Gradle and Maven** `io.synadia:scheduled-message` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) From 61a25ba94f5592e7ef0632caabb1cf8abfc3c2f6 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 26 Sep 2025 13:10:17 -0400 Subject: [PATCH 047/135] update readmes --- .github/workflows/bp-main.yml | 47 ++++++++++++++++++++++++++++++++ .github/workflows/bp-pr.yml | 41 ++++++++++++++++++++++++++++ .github/workflows/bp-release.yml | 39 ++++++++++++++++++++++++++ schedule-message/README.md | 4 +-- 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/bp-main.yml create mode 100644 .github/workflows/bp-pr.yml create mode 100644 .github/workflows/bp-release.yml diff --git a/.github/workflows/bp-main.yml b/.github/workflows/bp-main.yml new file mode 100644 index 0000000..8aff27c --- /dev/null +++ b/.github/workflows/bp-main.yml @@ -0,0 +1,47 @@ +name: Batch Publish Main Snapshot + +on: + push: + branches: + - main + paths: + - 'batch-publish/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd batch-publish + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd batch-publish + ./gradlew javadoc + popd + - name: Publish Snapshot + run: | + pushd batch-publish + ./gradlew -i publishToSonatype + popd diff --git a/.github/workflows/bp-pr.yml b/.github/workflows/bp-pr.yml new file mode 100644 index 0000000..8a0f9f5 --- /dev/null +++ b/.github/workflows/bp-pr.yml @@ -0,0 +1,41 @@ +name: Batch Publish Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'batch-publish/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd batch-publish + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd batch-publish + ./gradlew javadoc + popd diff --git a/.github/workflows/bp-release.yml b/.github/workflows/bp-release.yml new file mode 100644 index 0000000..5bb780d --- /dev/null +++ b/.github/workflows/bp-release.yml @@ -0,0 +1,39 @@ +name: Batch Publish Release + +on: + push: + tags: [ 'bp/*' ] + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: "release" + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd batch-publish + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify, Sign and Publish Release + run: | + pushd batch-publish + ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + popd diff --git a/schedule-message/README.md b/schedule-message/README.md index 3f36760..26e1a8b 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -7,8 +7,8 @@ Eventually the ability to schedule a message to publish based on a cron or sched https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md -**Current Release**: 0.1.0 -  **Current Snapshot**: 0.1.1-SNAPSHOT +**Current Release**: 0.0.1 +  **Current Snapshot**: 0.0.2-SNAPSHOT   **Gradle and Maven** `io.synadia:scheduled-message` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) From dcdf0fdc2376f7a7f497d5a9b69e15c0d12d7ca5 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 26 Sep 2025 13:17:36 -0400 Subject: [PATCH 048/135] update gradles --- batch-publish/build.gradle | 2 +- counter/build.gradle | 2 +- schedule-message/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index 245d0a4..a065e62 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.2.0" +def jarVersion = "0.2.1" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/counter/build.gradle b/counter/build.gradle index ed6381f..24b2ed5 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -39,7 +39,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.23.0' - implementation 'io.synadia:direct-batch:0.1.3-SNAPSHOT' + implementation 'io.synadia:direct-batch:0.1.3' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'commons-codec:commons-codec:1.18.0' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 953fb23..8fd127c 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.1" +def jarVersion = "0.0.2" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From bbb43331b7fae25b1413bc8de0c1fac152ce4a60 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 26 Sep 2025 13:50:14 -0400 Subject: [PATCH 049/135] update readme and gradle after release --- counter/build.gradle | 2 +- direct-batch/README.md | 4 ++-- direct-batch/build.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/counter/build.gradle b/counter/build.gradle index 24b2ed5..88c37dd 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.1.0" +def jarVersion = "0.1.1" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/direct-batch/README.md b/direct-batch/README.md index b252811..1d8926a 100644 --- a/direct-batch/README.md +++ b/direct-batch/README.md @@ -8,8 +8,8 @@ It only works with the 2.11.x NATS Server and the JNats 2.20.5.main-2-11-SNAPSHO The direct batch functionality leverages the direct message capabilities introduced in NATS Server 2.11 The functionality is described in [ADR-31](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-31.md) -**Current Release**: 0.1.2 - **Current Snapshot**: 0.1.3-SNAPSHOT +**Current Release**: 0.1.3 + **Current Snapshot**: 0.1.4-SNAPSHOT   **Gradle and Maven** `io.synadia:direct-batch` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 0e852e9..724ceb3 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.1.3" +def jarVersion = "0.1.4" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From 09b484de9f19122888c9cedadb9a91ac242794c8 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 26 Sep 2025 14:50:56 -0400 Subject: [PATCH 050/135] update readme --- README.md | 29 ++++++++++++++++++++++++++++- counter/README.md | 1 - encoded-kv/README.md | 4 ++-- js-publish-extensions/README.md | 2 +- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 286899c..a2c8309 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,21 @@ not made until the specific project has a v1.0.0 version. Extension for retrying anything. +**Current Release**: 0.2.1 +  **Current Snapshot**: 0.2.2-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=retrier/README.md)](retrier/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:retrier-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/retrier/javadoc.svg)](https://javadoc.io/doc/io.synadia/retrier) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier) -## JS Publish Extensions +## Jetstream Publish Extensions General extensions for Jetstream Publishing +**Current Release**: 0.4.3 +  **Current Snapshot**: 0.4.3-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=js-publish-extensions/README.md)](js-publish-extensions/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:jnats--js--publish--extensions-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/jnats-js-publish-extensions/javadoc.svg)](https://javadoc.io/doc/io.synadia/jnats-js-publish-extensions) @@ -31,6 +37,9 @@ General extensions for Jetstream Publishing Extension to get many responses for a single core request. +**Current Release**: 0.1.0 +  **Current Snapshot**: 0.1.1-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=request-many/README.md)](request-many/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:request--many-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/request-many/javadoc.svg)](https://javadoc.io/doc/io.synadia/request-many) @@ -40,6 +49,9 @@ Extension to get many responses for a single core request. Extension over Key Value to allow custom encoding of keys and values. +**Current Release**: 0.0.1 +  **Current Snapshot**: 0.0.2-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=encoded-kv/README.md)](encoded-kv/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/encoded-kv/javadoc.svg)](https://javadoc.io/doc/io.synadia/encoded-kv) @@ -50,6 +62,9 @@ Extension over Key Value to allow custom encoding of keys and values. The direct batch functionality leverages the direct message capabilities introduced in NATS Server v2.11. The functionality is described in [ADR-31](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-31.md) +**Current Release**: 0.1.3 + **Current Snapshot**: 0.1.4-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=direct-batch/README.md)](direct-batch/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:direct--batch-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/direct-batch/javadoc.svg)](https://javadoc.io/doc/io.synadia/direct-batch) @@ -59,6 +74,9 @@ The functionality is described in [ADR-31](https://github.com/nats-io/nats-archi Run some NATS servers and cause chaos by bringing them up and down. +**Current Release**: 0.0.3 +  **Current Snapshot**: 0.0.4-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](chaos-runner/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) @@ -68,6 +86,9 @@ Run some NATS servers and cause chaos by bringing them up and down. Utility to publish an atomic batch, a group of up to 1000 messages +**Current Release**: 0.2.0 + **Current Snapshot**: 0.2.1-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=batch-publish/README.md)](batch-publish/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/batch-publish/javadoc.svg)](https://javadoc.io/doc/io.synadia/batch-publish) @@ -77,6 +98,9 @@ Utility to publish an atomic batch, a group of up to 1000 messages Utility to take advantage of the distributed counter functionality. +**Current Release**: 0.1.0 +  **Current Snapshot**: 0.1.1-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=counter/README.md)](counter/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counter-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/counter/javadoc.svg)](https://javadoc.io/doc/io.synadia/counter) @@ -87,6 +111,9 @@ Utility to take advantage of the distributed counter functionality. Utility to leverage the ability to schedule a message to be published at a later time. Eventually the ability to schedule a message to publish based on a cron or schedule. +**Current Release**: 0.0.1 +  **Current Snapshot**: 0.0.2-SNAPSHOT + [![README](https://img.shields.io/badge/README-blue?style=flat&link=scheduled-message/README.md)](scheduled-message/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) diff --git a/counter/README.md b/counter/README.md index 567cbc0..e85b642 100644 --- a/counter/README.md +++ b/counter/README.md @@ -6,7 +6,6 @@ Utility to take advantage of the distributed counter functionality. https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md - **Current Release**: 0.1.0   **Current Snapshot**: 0.1.1-SNAPSHOT   **Gradle and Maven** `io.synadia:counter` diff --git a/encoded-kv/README.md b/encoded-kv/README.md index fb074df..0be4f95 100644 --- a/encoded-kv/README.md +++ b/encoded-kv/README.md @@ -14,8 +14,8 @@ It requires a _codec_, which * decodes the encoded key back to the key object * decodes the encoded data bytes back into the value object. -**Current Release**: N/A -  **Current Snapshot**: 0.1.0-SNAPSHOT +**Current Release**: 0.0.1 +  **Current Snapshot**: 0.0.2-SNAPSHOT   **Gradle and Maven** `io.synadia:encoded-kv` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/js-publish-extensions/README.md b/js-publish-extensions/README.md index 6eb30a4..31f2969 100644 --- a/js-publish-extensions/README.md +++ b/js-publish-extensions/README.md @@ -4,7 +4,7 @@ Extensions specific to JetStream publishing. -**Current Release**: 0.4.2 +**Current Release**: 0.4.3   **Current Snapshot**: 0.4.3-SNAPSHOT   **Gradle and Maven** `io.synadia:jnats-js-publish-extensions` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) From fa6f085d97d6fca1d55b9b3b9e978392ef5b40fa Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 26 Sep 2025 14:52:30 -0400 Subject: [PATCH 051/135] update readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a2c8309..9b5dbf5 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ Orbit.java is a set of independent utilities or extensions around the [JNATS](https://github.com/nats-io/nats.java) ecosystem that aims to boost productivity and provide a higher abstraction layer for the [JNATS](https://github.com/nats-io/nats.java) -client. Note that these libraries will evolve rapidly and API guarantees are -not made until the specific project has a v1.0.0 version. +client. Note that these libraries will evolve rapidly and API guarantees are general not made until the specific project has a v1.0.0 version. # Utilities From fcc1af5f2f8082ac14dd74147998538041bc11d2 Mon Sep 17 00:00:00 2001 From: Piotr Piotrowski Date: Thu, 2 Oct 2025 14:21:39 +0200 Subject: [PATCH 052/135] fix readme link Signed-off-by: Piotr Piotrowski --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b5dbf5..21a8ba5 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ Eventually the ability to schedule a message to publish based on a cron or sched **Current Release**: 0.0.1   **Current Snapshot**: 0.0.2-SNAPSHOT -[![README](https://img.shields.io/badge/README-blue?style=flat&link=scheduled-message/README.md)](scheduled-message/README.md) +[![README](https://img.shields.io/badge/README-blue?style=flat&link=schedule-message/README.md)](schedule-message/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) From 3e98e511cf409f0b0850e27fb9b9fe8bd22b97ce Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 2 Oct 2025 10:48:40 -0400 Subject: [PATCH 053/135] Ability to only bring down a server for a specific port --- .../ChaosRunnerSpecificPortExample.java | 128 ++++++++++++++++++ .../java/io/synadia/chaos/ChaosArguments.java | 9 ++ .../java/io/synadia/chaos/ChaosRunner.java | 32 ++++- 3 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerSpecificPortExample.java diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerSpecificPortExample.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerSpecificPortExample.java new file mode 100644 index 0000000..7a8a078 --- /dev/null +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerSpecificPortExample.java @@ -0,0 +1,128 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.Options; +import io.synadia.chaos.ChaosArguments; +import io.synadia.chaos.ChaosRunner; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import static io.synadia.chaos.ChaosUtils.out; + +public class ChaosRunnerSpecificPortExample { + private static final int SPECIFIC_PORT = 4222; + private static final int SERVER_COUNT = 3; // 1, 3, 5 + private static final long DELAY = 3000; // the delay to bring a server down + private static final long INITIAL_DELAY = 3000; // the delay to bring a server down the first time + private static final long DOWN_TIME = 3000; // how long before bringing the server up + private static final int HEALTH_CHECK_DELAY = 1000; + + private static final int NUM_CONNECTIONS = 5; + + public static void main(String[] args) throws Exception { + ChaosArguments arguments = new ChaosArguments() + .servers(SERVER_COUNT) + .specificPort(SPECIFIC_PORT) + .workDirectory("C:\\temp\\chaos-runner") + .serverNamePrefix("cr-example-server") + .clusterName("cr-example-cluster") + .delay(DELAY) + .initialDelay(INITIAL_DELAY) + .downTime(DOWN_TIME) + // this is done last so anything on the command line + // is used over the hard coded items. + .args(args); + + ChaosRunner runner = ChaosRunner.start(arguments); + + // just give the servers a little time to be ready be first connect + Thread.sleep(1000); + + String[] urls = runner.getConnectionUrls(); + out("Connection Urls"); + for (String url : urls) { + out(" ", url); + } + + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + List connections = new ArrayList<>(urls.length); + for (int i = 0; i < NUM_CONNECTIONS; i++) { + String connectionName = "Conn" + (i + 1); + Options options = Options.builder().servers(urls) + .connectionListener(new ChaosConnectionListener(connectionName)) + .errorListener(new ChaosErrorListener(connectionName)) + .build(); + + Connection connection = Nats.connect(options); + connections.add(connection); + } + + int[] ports = runner.getConnectionPorts(); + int[] monitorPorts = runner.getMonitorPorts(); + boolean hasMonitor = monitorPorts[0] > 0; + + String[] hzs = new String[ports.length]; + while (true) { + Thread.sleep(HEALTH_CHECK_DELAY); + if (hasMonitor) { + boolean changed = false; + for (int i = 0; i < monitorPorts.length; i++) { + String hz = readHealthz(monitorPorts[i]); + if (!hz.equals(hzs[i])) { + changed = true; + hzs[i] = hz; + } + } + if (changed) { + out("HealthZ"); + for (int i = 0; i < monitorPorts.length; i++) { + int port = ports[i]; + int mport = monitorPorts[i]; + out(" ", port + "/" + mport, hzs[i]); + } + } + } + } + } + + private static String readHealthz(int port) { + return readEndpoint(port, "healthz"); + } + + private static String readEndpoint(int port, String endpoint) { + String sUrl = "http://localhost:" + port + "/" + endpoint; + try { + URL url = new URL(sUrl); + InputStream inputStream = url.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + + boolean first = true; + String line; + StringBuilder content = new StringBuilder(); + while ((line = reader.readLine()) != null) { + if (first) { + first = false; + } + else { + content.append(System.lineSeparator()); + } + content.append(line); + } + reader.close(); + return content.toString().trim(); + } + catch (IOException e) { + return e.getMessage(); + } + } +} diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java index 7b5e1fd..c2951dd 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java @@ -18,6 +18,7 @@ public class ChaosArguments { long delay = 5_000; long downTime = 5_000; boolean random = false; + int specificPort = -1; int port = 4222; int listen = 4232; int monitor = 4282; @@ -92,6 +93,11 @@ public ChaosArguments port(int port) { return this; } + public ChaosArguments specificPort(int port) { + this.specificPort = port; + return this; + } + public ChaosArguments listen(int listen) { this.listen = listen; return this; @@ -138,6 +144,9 @@ public ChaosArguments args(String[] args) { case "--port": port(Integer.parseInt(args[++x])); break; + case "--sport": + specificPort(Integer.parseInt(args[++x])); + break; case "--listen": listen(Integer.parseInt(args[++x])); break; diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index cc6eeab..dc382e2 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -37,6 +37,7 @@ public class ChaosRunner { public final long delay; public final long downTime; public final boolean random; + public final int specificPort; public final int port; public final int listen; public final int monitor; @@ -101,15 +102,23 @@ private void scheduleUp() { private void downTask() { try { - if (random) { + if (specificPort != -1) { + for (int i = 0; i < natsServerRunners.size(); i++) { + NatsServerRunner nsr = natsServerRunners.get(i); + if (nsr.getPort() == specificPort) { + downIx = i; + break; + } + } + } + else if (random) { downIx = ThreadLocalRandom.current().nextInt(servers); } NatsServerRunner runner = natsServerRunners.remove(downIx); - printer.out(CR_LABEL, "DOWN", runner.getPort()); + printer.out(CR_LABEL, "DOWN", runner.getPort()); clusterInserts.add(clusterInserts.remove(downIx)); runner.close(); - scheduleUp(); } catch (Throwable e) { @@ -179,12 +188,16 @@ else if (!a.workDirectory.toFile().exists()) { this.delay = a.delay; this.downTime = a.downTime; this.random = a.random; + this.specificPort = a.specificPort; this.port = a.port; this.listen = a.listen; this.monitor = a.monitor; natsServerRunners = new ArrayList<>(); if (servers == 1) { + if (specificPort != -1 && specificPort != port) { + throw new IllegalArgumentException("Invalid specific port"); + } List inserts = new ArrayList<>(); ClusterNode cn; Path jsStorePath = Paths.get(jsStoreDirBase.toString(), "" + port); @@ -217,6 +230,19 @@ else if (!a.workDirectory.toFile().exists()) { } else { List cns = createNodes(servers, clusterName, serverNamePrefix, jsStoreDirBase, DEFAULT_HOST, port, listen, monitor < 1 ? null : monitor); + if (specificPort != -1) { + boolean found = false; + for (ClusterNode cn : cns) { + if (cn.port == specificPort) { + found = true; + break; + } + } + if (!found) { + throw new IllegalArgumentException("Invalid specific port"); + } + } + clusterInserts = createClusterInserts(cns); } From f65405155d2cafb1103e609da6b85bfd34400714 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 2 Oct 2025 10:57:34 -0400 Subject: [PATCH 054/135] update CR version for release --- chaos-runner/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index ddde221..e8cf2fe 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.5" +def jarVersion = "0.0.6" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From eeefdfc339b36bbcda5bb01c349af6965007a90b Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 3 Oct 2025 14:38:39 -0400 Subject: [PATCH 055/135] chaos runner shutdown improvements --- chaos-runner/build.gradle | 2 +- .../synadia/examples/ChaosRunnerShutdown.java | 94 +++++++++++++++++++ .../java/io/synadia/chaos/ChaosRunner.java | 85 ++++++++++++++--- 3 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerShutdown.java diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index e8cf2fe..be1c3af 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.6" +def jarVersion = "0.0.7" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerShutdown.java b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerShutdown.java new file mode 100644 index 0000000..5715a50 --- /dev/null +++ b/chaos-runner/src/examples/java/io/synadia/examples/ChaosRunnerShutdown.java @@ -0,0 +1,94 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.synadia.chaos.ChaosArguments; +import io.synadia.chaos.ChaosRunner; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; + +import static io.synadia.chaos.ChaosUtils.out; + +public class ChaosRunnerShutdown { + + public static void main(String[] args) throws Exception { + ChaosArguments arguments = new ChaosArguments() + .servers(3) + .workDirectory("C:\\temp\\chaos-runner") + .serverNamePrefix("cr-shutdown-server") + .clusterName("cr-shutdown-cluster") + .delay(30_000) + .initialDelay(30_000) + .downTime(30_000); + + ChaosRunner runner = ChaosRunner.start(arguments); + + // just give the servers a little time to be ready be first connect + Thread.sleep(1000); + + String[] urls = runner.getConnectionUrls(); + out("Connection Urls"); + for (String url : urls) { + out(" ", url); + } + + int[] ports = runner.getConnectionPorts(); + int[] monitorPorts = runner.getMonitorPorts(); + + Thread.sleep(1000); + out("H RUNNING", ChaosRunner.isRunning()); + for (int i = 0; i < monitorPorts.length; i++) { + int port = ports[i]; + int mport = monitorPorts[i]; + String hz = readHealthz(monitorPorts[i]); + out("H", port + "/" + mport, hz); + } + + ChaosRunner.shutdown(); + + Thread.sleep(1000); + out("Z RUNNING", ChaosRunner.isRunning()); + for (int i = 0; i < monitorPorts.length; i++) { + int port = ports[i]; + int mport = monitorPorts[i]; + String hz = readHealthz(monitorPorts[i]); + out("Z", port + "/" + mport, hz); + } + } + + private static String readHealthz(int port) { + return readEndpoint(port, "healthz"); + } + + private static String readEndpoint(int port, String endpoint) { + String sUrl = "http://localhost:" + port + "/" + endpoint; + try { + URL url = new URL(sUrl); + InputStream inputStream = url.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + + boolean first = true; + String line; + StringBuilder content = new StringBuilder(); + while ((line = reader.readLine()) != null) { + if (first) { + first = false; + } + else { + content.append(System.lineSeparator()); + } + content.append(line); + } + reader.close(); + return content.toString().trim(); + } + catch (IOException e) { + return e.getMessage(); + } + } +} diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index dc382e2..59682bf 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -16,6 +16,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import static io.nats.NatsRunnerUtils.*; @@ -25,6 +26,8 @@ public class ChaosRunner { private static final String CR_LABEL = "ChaosRunner"; + private static final ReentrantLock INSTANCE_LOCK = new ReentrantLock(); + private static Thread APP_SHUTDOWN_HOOK_THREAD; private static ChaosRunner INSTANCE; public final ChaosPrinter printer; @@ -282,6 +285,7 @@ public static ChaosRunner start(ChaosArguments a, ChaosPrinter printer) { NatsServerRunner.setDefaultOutputLevel(Level.SEVERE); final ChaosPrinter finalPrinter = printer == null ? getDefaultPrinter() : printer; + INSTANCE_LOCK.lock(); try { INSTANCE = new ChaosRunner(a, finalPrinter); } @@ -289,28 +293,81 @@ public static ChaosRunner start(ChaosArguments a, ChaosPrinter printer) { finalPrinter.err(CR_LABEL, "Failed to start ChaosRunner", e); System.exit(-1); } + finally { + INSTANCE_LOCK.unlock(); + } - Runtime.getRuntime().addShutdownHook( - new Thread("app-shutdown-hook") { - @Override - public void run() { - INSTANCE.shutdown(); - finalPrinter.out(CR_LABEL, "EXIT"); - } - }); + APP_SHUTDOWN_HOOK_THREAD = new Thread("app-shutdown-hook") { + @Override + public void run() { + shutdownServers(); + shutdownExecutor(); + finalPrinter.out(CR_LABEL, "EXIT"); + } + }; + + Runtime.getRuntime().addShutdownHook(APP_SHUTDOWN_HOOK_THREAD); return INSTANCE; } - private void shutdown() { + public static boolean isRunning() { + INSTANCE_LOCK.lock(); + try { + return INSTANCE != null; + } + finally { + INSTANCE_LOCK.unlock(); + } + } + + public static void shutdown() { + INSTANCE_LOCK.lock(); + try { + removeShutdownHook(); + shutdownExecutor(); + shutdownServers(); + } + finally { + INSTANCE_LOCK.unlock(); + } + } + + public static void shutdownExecutor() { + INSTANCE_LOCK.lock(); try { - for (NatsServerRunner runner : natsServerRunners ) { - try { - runner.close(); + INSTANCE.executor.shutdown(); + } + finally { + INSTANCE_LOCK.unlock(); + } + } + + private static void removeShutdownHook() { + INSTANCE_LOCK.lock(); + try { + if (APP_SHUTDOWN_HOOK_THREAD != null) { + Runtime.getRuntime().removeShutdownHook(APP_SHUTDOWN_HOOK_THREAD); + APP_SHUTDOWN_HOOK_THREAD = null; + } + } + finally { + INSTANCE_LOCK.unlock(); + } + } + + private static void shutdownServers() { + INSTANCE_LOCK.lock(); + try { + if (INSTANCE != null) { + for (NatsServerRunner runner : INSTANCE.natsServerRunners) { + try { runner.close(); } catch (Exception ignore) {} } - catch (Exception ignore) {} + INSTANCE = null; } } - catch (Exception ignore) {} + finally { + INSTANCE_LOCK.unlock(); + } } } From 2645540f83855050409b3a6479829d1a8e74226a Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 4 Oct 2025 08:29:47 -0400 Subject: [PATCH 056/135] Update JNATS client --- batch-publish/build.gradle | 2 +- chaos-runner/build.gradle | 4 ++-- counter/build.gradle | 2 +- direct-batch/build.gradle | 2 +- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- schedule-message/build.gradle | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index a065e62..e9bb5ab 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index be1c3af..c1fe1bd 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.7" +def jarVersion = "0.0.8" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" @@ -41,7 +41,7 @@ dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' // this is only for the example and the uber jar won't include it - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/counter/build.gradle b/counter/build.gradle index 88c37dd..e9e7769 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' implementation 'io.synadia:direct-batch:0.1.3' implementation 'org.jspecify:jspecify:1.0.0' diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 724ceb3..96db226 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index d02096a..47a678c 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index 03ecce8..ed75313 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index 67a5aa0..6b887f4 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index bc5b905..26c31dc 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 8fd127c..4e2c07b 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.23.1-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'commons-codec:commons-codec:1.18.0' From b9bd74da2bab226ac44f05d4c4fd3a6b0c1b13f0 Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 4 Oct 2025 08:30:19 -0400 Subject: [PATCH 057/135] Update batch publish readme --- batch-publish/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/batch-publish/README.md b/batch-publish/README.md index c62a804..655a553 100644 --- a/batch-publish/README.md +++ b/batch-publish/README.md @@ -4,6 +4,11 @@ Utility to publish an atomic batch, a group of up to 1000 messages +### Important + +* Messages are stored in memory on the server until the commit. +* Batch currently is not about speed, it's about transaction, meaning all the messages must be added to the stream or none of them do. + https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-50.md **Current Release**: 0.2.0 From 5e8a4b2f2191260464aacc9e3e461ae6ea18c2d5 Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 4 Oct 2025 08:31:03 -0400 Subject: [PATCH 058/135] direct batch re-uses MessageInforHandler, doesn't need its own --- .../io/synadia/direct/MessageInfoHandler.java | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 direct-batch/src/main/java/io/synadia/direct/MessageInfoHandler.java diff --git a/direct-batch/src/main/java/io/synadia/direct/MessageInfoHandler.java b/direct-batch/src/main/java/io/synadia/direct/MessageInfoHandler.java deleted file mode 100644 index 9a56a4d..0000000 --- a/direct-batch/src/main/java/io/synadia/direct/MessageInfoHandler.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.synadia.direct; - -import io.nats.client.api.MessageInfo; - -/** - * Handler for {@link MessageInfo}. - */ -public interface MessageInfoHandler { - /** - * Called to deliver a {@link MessageInfo} to the handler. - * - * @param messageInfo the received {@link MessageInfo} - */ - void onMessageInfo(MessageInfo messageInfo); -} From 48dd036459f4ebda2c6ce0c45d1077e74a5842d6 Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 4 Oct 2025 08:31:31 -0400 Subject: [PATCH 059/135] Chaos Runner better instance management --- .../java/io/synadia/chaos/ChaosArguments.java | 42 +++++++++++++++++ .../java/io/synadia/chaos/ChaosRunner.java | 45 +++++++++++-------- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java index c2951dd..00053b0 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosArguments.java @@ -3,6 +3,7 @@ import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Objects; import static io.nats.NatsRunnerUtils.DEFAULT_CLUSTER_NAME; import static io.nats.NatsRunnerUtils.DEFAULT_SERVER_NAME_PREFIX; @@ -220,4 +221,45 @@ public int getListen() { public int getMonitor() { return monitor; } + + @Override + public final boolean equals(Object o) { + if (!(o instanceof ChaosArguments)) return false; + if (this == o) { + return true; + } + + ChaosArguments that = (ChaosArguments) o; + return servers == that.servers + && js == that.js + && initialDelay == that.initialDelay + && delay == that.delay + && downTime == that.downTime + && random == that.random + && specificPort == that.specificPort + && port == that.port + && listen == that.listen + && monitor == that.monitor + && Objects.equals(clusterName, that.clusterName) + && Objects.equals(serverNamePrefix, that.serverNamePrefix) + && Objects.equals(workDirectory, that.workDirectory); + } + + @Override + public int hashCode() { + int result = servers; + result = 31 * result + Objects.hashCode(clusterName); + result = 31 * result + Objects.hashCode(serverNamePrefix); + result = 31 * result + Boolean.hashCode(js); + result = 31 * result + Objects.hashCode(workDirectory); + result = 31 * result + Long.hashCode(initialDelay); + result = 31 * result + Long.hashCode(delay); + result = 31 * result + Long.hashCode(downTime); + result = 31 * result + Boolean.hashCode(random); + result = 31 * result + specificPort; + result = 31 * result + port; + result = 31 * result + listen; + result = 31 * result + monitor; + return result; + } } diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index 59682bf..ce17179 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -27,8 +27,9 @@ public class ChaosRunner { private static final String CR_LABEL = "ChaosRunner"; private static final ReentrantLock INSTANCE_LOCK = new ReentrantLock(); - private static Thread APP_SHUTDOWN_HOOK_THREAD; private static ChaosRunner INSTANCE; + private static ChaosArguments INSTANCE_ARGUMENTS; + private static Thread APP_SHUTDOWN_HOOK_THREAD; public final ChaosPrinter printer; public final int servers; @@ -273,41 +274,47 @@ else if (!a.workDirectory.toFile().exists()) { scheduleDown(initialDelay); } - public static void main(String[] args) { - start(new ChaosArguments().args(args)); - } - - public static ChaosRunner start(ChaosArguments a) { + public static ChaosRunner start(ChaosArguments a) throws Exception { return start(a, null); } - public static ChaosRunner start(ChaosArguments a, ChaosPrinter printer) { + public static ChaosRunner start(ChaosArguments a, ChaosPrinter printer) throws Exception { NatsServerRunner.setDefaultOutputLevel(Level.SEVERE); final ChaosPrinter finalPrinter = printer == null ? getDefaultPrinter() : printer; INSTANCE_LOCK.lock(); try { + if (INSTANCE != null) { + if (INSTANCE_ARGUMENTS.equals(a)) { + // same arguments, just return the instance + return INSTANCE; + } + + throw new Exception("Instance already started with different arguments."); + } + INSTANCE = new ChaosRunner(a, finalPrinter); + + APP_SHUTDOWN_HOOK_THREAD = new Thread("app-shutdown-hook") { + @Override + public void run() { + shutdownServers(); + shutdownExecutor(); + finalPrinter.out(CR_LABEL, "EXIT"); + } + }; + + Runtime.getRuntime().addShutdownHook(APP_SHUTDOWN_HOOK_THREAD); + } catch (IOException e) { finalPrinter.err(CR_LABEL, "Failed to start ChaosRunner", e); - System.exit(-1); + throw e; } finally { INSTANCE_LOCK.unlock(); } - APP_SHUTDOWN_HOOK_THREAD = new Thread("app-shutdown-hook") { - @Override - public void run() { - shutdownServers(); - shutdownExecutor(); - finalPrinter.out(CR_LABEL, "EXIT"); - } - }; - - Runtime.getRuntime().addShutdownHook(APP_SHUTDOWN_HOOK_THREAD); - return INSTANCE; } From 8fdd485a6ef2ce4927af45f1a5abcfbc0ed19952 Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 4 Oct 2025 08:31:43 -0400 Subject: [PATCH 060/135] Counter Iterator --- .../io/synadia/examples/CounterExample.java | 25 +++++ .../main/java/io/synadia/counter/Counter.java | 29 +++++- .../io/synadia/counter/CounterIterator.java | 98 +++++++------------ 3 files changed, 88 insertions(+), 64 deletions(-) diff --git a/counter/src/examples/java/io/synadia/examples/CounterExample.java b/counter/src/examples/java/io/synadia/examples/CounterExample.java index a0fd427..7342295 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterExample.java @@ -9,10 +9,14 @@ import io.nats.client.Nats; import io.nats.client.api.StorageType; import io.nats.client.api.StreamConfiguration; +import io.nats.client.support.Debug; import io.synadia.counter.Counter; import io.synadia.counter.CounterEntryResponse; +import io.synadia.counter.CounterIterator; import java.math.BigInteger; +import java.time.Duration; +import java.util.Collections; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -163,6 +167,27 @@ public static void main(String[] args) throws Exception { er = eResponses.poll(10, TimeUnit.MILLISECONDS); } System.out.println(" " + er + " -> No more entries."); + + // ---------------------------------------------------------------------------------------------------- + System.out.println("\n8.1: iterateEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get via CounterIterator for multiple subjects."); + CounterIterator iterator = counter.iterateEntries("cs.A", "cs.B", "cs.C"); + while (iterator.hasNext()) { + Debug.info(" ", iterator.next()); + } + + System.out.println("\n8.2: iterateEntries(\"cs.*\") - Get via CounterIterator for wildcard subject(s)."); + iterator = counter.iterateEntries("cs.*"); + while (iterator.hasNext()) { + Debug.info(" ", iterator.next()); + } + + System.out.println("\n8.3: iterateEntries(\"cs.*\", timeoutFirst, timeoutSubsequent) - Get via CounterIterator with custom timeouts."); + Duration timeoutFirst = Duration.ofMillis(1000); + Duration timeoutSubsequent = Duration.ofMillis(200); + iterator = counter.iterateEntries(Collections.singletonList("cs.*"), timeoutFirst, timeoutSubsequent); + while (iterator.hasNext()) { + Debug.info(" ", iterator.next()); + } } } } diff --git a/counter/src/main/java/io/synadia/counter/Counter.java b/counter/src/main/java/io/synadia/counter/Counter.java index 95a9280..6cf697e 100644 --- a/counter/src/main/java/io/synadia/counter/Counter.java +++ b/counter/src/main/java/io/synadia/counter/Counter.java @@ -13,6 +13,7 @@ import java.io.IOException; import java.math.BigInteger; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; @@ -46,6 +47,8 @@ public static Counter createCounterStream(Connection conn, JetStreamOptions jso, } private final String streamName; + private final Connection conn; + private final Duration timeout; private final JetStreamManagement jsm; private final JetStream js; private final DirectBatchContext dbCtx; @@ -65,6 +68,17 @@ private Counter(@NonNull String streamName, @Nullable StreamInfo si ) throws IOException, JetStreamApiException { + this.conn = conn; + + Duration tempTimeout = null; + if (jso != null) { + tempTimeout = jso.getRequestTimeout(); + } + if (tempTimeout == null) { + tempTimeout = conn.getOptions().getConnectionTimeout(); + } + timeout = tempTimeout; + this.jsm = jsm == null ? conn.jetStreamManagement(jso) : jsm; js = this.jsm.jetStream(); @@ -168,10 +182,23 @@ public LinkedBlockingQueue getEntries(String... subjects) public LinkedBlockingQueue getEntries(List subjects) { LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); - dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterEntryResponse(mi))); + conn.getOptions().getExecutor().submit( + () -> dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterEntryResponse(mi)))); return queue; } + public CounterIterator iterateEntries(String... subjects) { + return new CounterIterator(getEntries(Arrays.asList(subjects)), timeout); + } + + public CounterIterator iterateEntries(List subjects) { + return new CounterIterator(getEntries(subjects), timeout); + } + + public CounterIterator iterateEntries(List subjects, Duration timeoutFirst, Duration timeoutSubsequent) { + return new CounterIterator(getEntries(subjects), timeoutFirst, timeoutSubsequent); + } + private static void validateSingleSubject(String subject) { if (subject == null || subject.isEmpty()) { throw new IllegalArgumentException("Subject required."); diff --git a/counter/src/main/java/io/synadia/counter/CounterIterator.java b/counter/src/main/java/io/synadia/counter/CounterIterator.java index 4889c04..fb9350d 100644 --- a/counter/src/main/java/io/synadia/counter/CounterIterator.java +++ b/counter/src/main/java/io/synadia/counter/CounterIterator.java @@ -3,85 +3,57 @@ package io.synadia.counter; -import io.nats.client.api.MessageInfo; -import io.synadia.direct.MessageInfoHandler; - -import java.math.BigInteger; +import java.time.Duration; import java.util.Iterator; -import java.util.NoSuchElementException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -public class CounterIterator implements Iterator, MessageInfoHandler { - private final LinkedBlockingQueue queue; - private final AtomicBoolean isFinished; - private BigInteger nextElement; - private boolean hasNextComputed; +public class CounterIterator implements Iterator { + private final LinkedBlockingQueue queue; + private final Duration timeoutFirst; + private final Duration timeoutSubsequent; + private CounterEntryResponse nextElement; + private boolean first; + private boolean terminated; - public CounterIterator() { - this.queue = new LinkedBlockingQueue<>(); - this.isFinished = new AtomicBoolean(false); - this.nextElement = null; - this.hasNextComputed = false; + public CounterIterator(LinkedBlockingQueue queue, Duration timeout) { + this(queue, timeout, timeout); } - @Override - public void onMessageInfo(MessageInfo messageInfo) { -// if (endMarker.equals(value)) { -// isFinished.set(true); -// } else { -// try { -// queue.put(value); -// } catch (InterruptedException e) { -// Thread.currentThread().interrupt(); -// throw new RuntimeException("Interrupted while adding value", e); -// } -// } + public CounterIterator(LinkedBlockingQueue queue, Duration timeoutFirst, Duration timeoutSubsequent) { + this.queue = queue; + this.timeoutFirst = timeoutFirst; + this.timeoutSubsequent = timeoutSubsequent; + first = true; + terminated = false; } @Override public boolean hasNext() { - if (!hasNextComputed) { - computeNext(); - } - return nextElement != null; - } - - @Override - public BigInteger next() { - if (!hasNext()) { - throw new NoSuchElementException("No more elements"); - } - BigInteger result = nextElement; - hasNextComputed = false; - nextElement = null; - return result; - } - - private void computeNext() { - if (hasNextComputed) { - return; - } - try { - // If we've already seen the end marker, no more elements - if (isFinished.get() && queue.isEmpty()) { - nextElement = null; - } else { - // Wait for next element with timeout to avoid infinite blocking - nextElement = queue.poll(100, TimeUnit.MILLISECONDS); - - // If queue is empty but not finished, keep polling - while (nextElement == null && !isFinished.get()) { - nextElement = queue.poll(100, TimeUnit.MILLISECONDS); + if (terminated) { + return false; + } + if (nextElement == null) { + nextElement = queue.poll(first ? timeoutFirst.toNanos() : timeoutSubsequent.toNanos(), TimeUnit.NANOSECONDS); + first = false; + if (nextElement != null && !nextElement.isEntry()) { + nextElement = null; + terminated = true; } } - } catch (InterruptedException e) { + return nextElement != null; + } + catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new RuntimeException("Interrupted while waiting for next element", e); + return false; } + } - hasNextComputed = true; + @Override + public CounterEntryResponse next() { + CounterEntryResponse current = nextElement; + nextElement = null; + return current; } } From 6cf53885d1973e97faf346027129180dd4c88fdc Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 4 Oct 2025 17:47:39 -0400 Subject: [PATCH 061/135] Counter Iterator --- .../examples/java/io/synadia/examples/CounterExample.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/counter/src/examples/java/io/synadia/examples/CounterExample.java b/counter/src/examples/java/io/synadia/examples/CounterExample.java index 7342295..8d24a28 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterExample.java +++ b/counter/src/examples/java/io/synadia/examples/CounterExample.java @@ -9,7 +9,6 @@ import io.nats.client.Nats; import io.nats.client.api.StorageType; import io.nats.client.api.StreamConfiguration; -import io.nats.client.support.Debug; import io.synadia.counter.Counter; import io.synadia.counter.CounterEntryResponse; import io.synadia.counter.CounterIterator; @@ -172,13 +171,13 @@ public static void main(String[] args) throws Exception { System.out.println("\n8.1: iterateEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get via CounterIterator for multiple subjects."); CounterIterator iterator = counter.iterateEntries("cs.A", "cs.B", "cs.C"); while (iterator.hasNext()) { - Debug.info(" ", iterator.next()); + System.out.println(" " + iterator.next()); } System.out.println("\n8.2: iterateEntries(\"cs.*\") - Get via CounterIterator for wildcard subject(s)."); iterator = counter.iterateEntries("cs.*"); while (iterator.hasNext()) { - Debug.info(" ", iterator.next()); + System.out.println(" " + iterator.next()); } System.out.println("\n8.3: iterateEntries(\"cs.*\", timeoutFirst, timeoutSubsequent) - Get via CounterIterator with custom timeouts."); @@ -186,7 +185,7 @@ public static void main(String[] args) throws Exception { Duration timeoutSubsequent = Duration.ofMillis(200); iterator = counter.iterateEntries(Collections.singletonList("cs.*"), timeoutFirst, timeoutSubsequent); while (iterator.hasNext()) { - Debug.info(" ", iterator.next()); + System.out.println(" " + iterator.next()); } } } From eeeca9da08f4c4f0d194b2ff305390375b5a23a7 Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 06:15:50 -0400 Subject: [PATCH 062/135] Fix Automatic-Module-Name(s) --- batch-publish/build.gradle | 2 +- chaos-runner/build.gradle | 2 +- direct-batch/build.gradle | 2 +- encoded-kv/README.md | 4 ++-- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- schedule-message/build.gradle | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index e9bb5ab..dba7fe4 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -71,7 +71,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.batch-publish') + attributes('Automatic-Module-Name': 'io.synadia.batch.publish') } bnd (['Implementation-Title': 'Batch Publish', 'Implementation-Version': jarVersion, diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index c1fe1bd..ca41b46 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -69,7 +69,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.chaos-runner') + attributes('Automatic-Module-Name': 'io.synadia.chaos.runner') } bnd (['Implementation-Title': 'Chaos Runner', 'Implementation-Version': jarVersion, diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 96db226..b6abc3c 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -71,7 +71,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.direct-batch') + attributes('Automatic-Module-Name': 'io.synadia.direct.batch') } bnd (['Implementation-Title': 'Direct Batch', 'Implementation-Version': jarVersion, diff --git a/encoded-kv/README.md b/encoded-kv/README.md index 0be4f95..c040431 100644 --- a/encoded-kv/README.md +++ b/encoded-kv/README.md @@ -14,8 +14,8 @@ It requires a _codec_, which * decodes the encoded key back to the key object * decodes the encoded data bytes back into the value object. -**Current Release**: 0.0.1 -  **Current Snapshot**: 0.0.2-SNAPSHOT +**Current Release**: 0.0.2 +  **Current Snapshot**: 0.0.3-SNAPSHOT   **Gradle and Maven** `io.synadia:encoded-kv` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index 47a678c..5d70642 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -67,7 +67,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.encoded-kv') + attributes('Automatic-Module-Name': 'io.synadia.encoded.kv') } bnd (['Implementation-Title': 'KV Encoding', 'Implementation-Version': jarVersion, diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index ed75313..f0b5672 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -74,7 +74,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.jnats-js-publish-extensions') + attributes('Automatic-Module-Name': 'io.synadia.jnats.js.publish.extensions') } bnd (['Implementation-Title': 'JNATS JetStream Publish Extensions', 'Implementation-Version': jarVersion, diff --git a/request-many/build.gradle b/request-many/build.gradle index 6b887f4..7f6fd64 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -66,7 +66,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.request-many') + attributes('Automatic-Module-Name': 'io.synadia.request.many') } bnd (['Implementation-Title': 'Request Many Utility', 'Implementation-Version': jarVersion, diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 4e2c07b..c7069a5 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -72,7 +72,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.schedule-message') + attributes('Automatic-Module-Name': 'io.synadia.schedule.message') } bnd (['Implementation-Title': 'JetStream Scheduled Messages', 'Implementation-Version': jarVersion, From 6705ee6bc7f65cb761f642ef37859b0544c6bf6b Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 06:16:14 -0400 Subject: [PATCH 063/135] Fix Automatic-Module-Name(s) --- README.md | 4 ++-- encoded-kv/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 21a8ba5..1be7353 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@ Extension to get many responses for a single core request. Extension over Key Value to allow custom encoding of keys and values. -**Current Release**: 0.0.1 -  **Current Snapshot**: 0.0.2-SNAPSHOT +**Current Release**: 0.0.2 +  **Current Snapshot**: 0.0.3-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=encoded-kv/README.md)](encoded-kv/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-00BC8E?labelColor=grey&style=flat) diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index 5d70642..a908530 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.2" +def jarVersion = "0.0.3" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From e377b552cc875bf1063eb3c7351735a832dda06d Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 06:40:16 -0400 Subject: [PATCH 064/135] Fix Automatic-Module-Name(s) --- .github/workflows/ekv-release.yml | 2 +- README.md | 8 ++++---- batch-publish/build.gradle | 2 +- chaos-runner/build.gradle | 2 +- counter/build.gradle | 2 +- direct-batch/build.gradle | 2 +- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- schedule-message/build.gradle | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ekv-release.yml b/.github/workflows/ekv-release.yml index 2cc62dc..dacdddc 100644 --- a/.github/workflows/ekv-release.yml +++ b/.github/workflows/ekv-release.yml @@ -1,4 +1,4 @@ -name: Publish Extensions Release +name: Encoded KV Release on: push: diff --git a/README.md b/README.md index 1be7353..0e94f6a 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@ Extension to get many responses for a single core request. Extension over Key Value to allow custom encoding of keys and values. -**Current Release**: 0.0.2 -  **Current Snapshot**: 0.0.3-SNAPSHOT +**Current Release**: 0.0.3 +  **Current Snapshot**: 0.0.4-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=encoded-kv/README.md)](encoded-kv/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-00BC8E?labelColor=grey&style=flat) @@ -110,8 +110,8 @@ Utility to take advantage of the distributed counter functionality. Utility to leverage the ability to schedule a message to be published at a later time. Eventually the ability to schedule a message to publish based on a cron or schedule. -**Current Release**: 0.0.1 -  **Current Snapshot**: 0.0.2-SNAPSHOT +**Current Release**: 0.0.2 +  **Current Snapshot**: 0.0.3-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=schedule-message/README.md)](schedule-message/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index dba7fe4..9b22435 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index ca41b46..9c240e2 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' // this is only for the example and the uber jar won't include it - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/counter/build.gradle b/counter/build.gradle index e9e7769..88c37dd 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'io.synadia:direct-batch:0.1.3' implementation 'org.jspecify:jspecify:1.0.0' diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index b6abc3c..2da2e5c 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index a908530..a560e99 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index f0b5672..bbf573e 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index 7f6fd64..cf69219 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index 26c31dc..bc5b905 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index c7069a5..210570c 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.1-SNAPSHOT' + implementation 'io.nats:jnats:2.23.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'commons-codec:commons-codec:1.18.0' From 75d606a6d39dbe3b80f39ee42e9a9f5eebc332fe Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 06:54:10 -0400 Subject: [PATCH 065/135] Fix Automatic-Module-Name(s) --- encoded-kv/README.md | 4 ++-- schedule-message/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/encoded-kv/README.md b/encoded-kv/README.md index c040431..b12af3c 100644 --- a/encoded-kv/README.md +++ b/encoded-kv/README.md @@ -14,8 +14,8 @@ It requires a _codec_, which * decodes the encoded key back to the key object * decodes the encoded data bytes back into the value object. -**Current Release**: 0.0.2 -  **Current Snapshot**: 0.0.3-SNAPSHOT +**Current Release**: 0.0.3 +  **Current Snapshot**: 0.0.4-SNAPSHOT   **Gradle and Maven** `io.synadia:encoded-kv` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/schedule-message/README.md b/schedule-message/README.md index 26e1a8b..29037c8 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -7,8 +7,8 @@ Eventually the ability to schedule a message to publish based on a cron or sched https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md -**Current Release**: 0.0.1 -  **Current Snapshot**: 0.0.2-SNAPSHOT +**Current Release**: 0.0.2 +  **Current Snapshot**: 0.0.3-SNAPSHOT   **Gradle and Maven** `io.synadia:scheduled-message` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) From 006c5350ac9de454662882b69a02e8fa096c76c4 Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 06:54:45 -0400 Subject: [PATCH 066/135] Fix Automatic-Module-Name(s) --- encoded-kv/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index a560e99..2861913 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.3" +def jarVersion = "0.0.4" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From b454bbccf5849eba4c3d88532a397f893b1b6544 Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 07:13:07 -0400 Subject: [PATCH 067/135] Fix Automatic-Module-Name(s) --- js-publish-extensions/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index bbf573e..f2088a8 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.4.3" +def jarVersion = "0.4.4" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From 1052e6b5ca3c7f78079bfc074267954faa190c04 Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 07:26:01 -0400 Subject: [PATCH 068/135] After Fix Automatic-Module-Name(s) --- README.md | 28 ++++++++++++++-------------- batch-publish/README.md | 4 ++-- batch-publish/build.gradle | 2 +- chaos-runner/README.md | 4 ++-- chaos-runner/build.gradle | 2 +- counter/README.md | 4 ++-- counter/build.gradle | 2 +- direct-batch/README.md | 4 ++-- direct-batch/build.gradle | 2 +- encoded-kv/README.md | 4 ++-- encoded-kv/build.gradle | 2 +- js-publish-extensions/README.md | 4 ++-- js-publish-extensions/build.gradle | 2 +- request-many/README.md | 4 ++-- request-many/build.gradle | 2 +- schedule-message/README.md | 1 - schedule-message/build.gradle | 2 +- 17 files changed, 36 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 0e94f6a..89b3c04 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Extension for retrying anything. General extensions for Jetstream Publishing -**Current Release**: 0.4.3 -  **Current Snapshot**: 0.4.3-SNAPSHOT +**Current Release**: 0.4.4 +  **Current Snapshot**: 0.4.5-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=js-publish-extensions/README.md)](js-publish-extensions/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:jnats--js--publish--extensions-00BC8E?labelColor=grey&style=flat) @@ -36,8 +36,8 @@ General extensions for Jetstream Publishing Extension to get many responses for a single core request. -**Current Release**: 0.1.0 -  **Current Snapshot**: 0.1.1-SNAPSHOT +**Current Release**: 0.1.1 +  **Current Snapshot**: 0.1.2-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=request-many/README.md)](request-many/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:request--many-00BC8E?labelColor=grey&style=flat) @@ -48,8 +48,8 @@ Extension to get many responses for a single core request. Extension over Key Value to allow custom encoding of keys and values. -**Current Release**: 0.0.3 -  **Current Snapshot**: 0.0.4-SNAPSHOT +**Current Release**: 0.0.4 +  **Current Snapshot**: 0.0.5-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=encoded-kv/README.md)](encoded-kv/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-00BC8E?labelColor=grey&style=flat) @@ -61,8 +61,8 @@ Extension over Key Value to allow custom encoding of keys and values. The direct batch functionality leverages the direct message capabilities introduced in NATS Server v2.11. The functionality is described in [ADR-31](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-31.md) -**Current Release**: 0.1.3 - **Current Snapshot**: 0.1.4-SNAPSHOT +**Current Release**: 0.1.4 + **Current Snapshot**: 0.1.5-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=direct-batch/README.md)](direct-batch/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:direct--batch-00BC8E?labelColor=grey&style=flat) @@ -73,8 +73,8 @@ The functionality is described in [ADR-31](https://github.com/nats-io/nats-archi Run some NATS servers and cause chaos by bringing them up and down. -**Current Release**: 0.0.3 -  **Current Snapshot**: 0.0.4-SNAPSHOT +**Current Release**: 0.0.8 +  **Current Snapshot**: 0.0.9-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](chaos-runner/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) @@ -85,8 +85,8 @@ Run some NATS servers and cause chaos by bringing them up and down. Utility to publish an atomic batch, a group of up to 1000 messages -**Current Release**: 0.2.0 - **Current Snapshot**: 0.2.1-SNAPSHOT +**Current Release**: 0.2.1 + **Current Snapshot**: 0.2.2-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=batch-publish/README.md)](batch-publish/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-00BC8E?labelColor=grey&style=flat) @@ -97,8 +97,8 @@ Utility to publish an atomic batch, a group of up to 1000 messages Utility to take advantage of the distributed counter functionality. -**Current Release**: 0.1.0 -  **Current Snapshot**: 0.1.1-SNAPSHOT +**Current Release**: 0.1.1 +  **Current Snapshot**: 0.1.2-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=counter/README.md)](counter/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counter-00BC8E?labelColor=grey&style=flat) diff --git a/batch-publish/README.md b/batch-publish/README.md index 655a553..992e2c1 100644 --- a/batch-publish/README.md +++ b/batch-publish/README.md @@ -11,8 +11,8 @@ Utility to publish an atomic batch, a group of up to 1000 messages https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-50.md -**Current Release**: 0.2.0 - **Current Snapshot**: 0.2.1-SNAPSHOT +**Current Release**: 0.2.1 + **Current Snapshot**: 0.2.2-SNAPSHOT   **Gradle and Maven** `io.synadia:batch-publish` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index 9b22435..5287bde 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.2.1" +def jarVersion = "0.2.2" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/chaos-runner/README.md b/chaos-runner/README.md index 3a51430..b3d6cae 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -5,8 +5,8 @@ A simple java program that can start 1 or more NATS Servers and then add chaos, by taking one of them down on a delay and bringing it back up after a downtime. -**Current Release**: 0.0.3 -  **Current Snapshot**: 0.0.4-SNAPSHOT +**Current Release**: 0.0.8 +  **Current Snapshot**: 0.0.9-SNAPSHOT   **Gradle and Maven** `io.synadia:chaos-runner` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 9c240e2..15fc83b 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.8" +def jarVersion = "0.0.9" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/counter/README.md b/counter/README.md index e85b642..745a1f6 100644 --- a/counter/README.md +++ b/counter/README.md @@ -6,8 +6,8 @@ Utility to take advantage of the distributed counter functionality. https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md -**Current Release**: 0.1.0 -  **Current Snapshot**: 0.1.1-SNAPSHOT +**Current Release**: 0.1.1 +  **Current Snapshot**: 0.1.2-SNAPSHOT   **Gradle and Maven** `io.synadia:counter` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/counter/build.gradle b/counter/build.gradle index 88c37dd..310e5cc 100644 --- a/counter/build.gradle +++ b/counter/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.1.1" +def jarVersion = "0.1.2" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/direct-batch/README.md b/direct-batch/README.md index 1d8926a..6e1b484 100644 --- a/direct-batch/README.md +++ b/direct-batch/README.md @@ -8,8 +8,8 @@ It only works with the 2.11.x NATS Server and the JNats 2.20.5.main-2-11-SNAPSHO The direct batch functionality leverages the direct message capabilities introduced in NATS Server 2.11 The functionality is described in [ADR-31](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-31.md) -**Current Release**: 0.1.3 - **Current Snapshot**: 0.1.4-SNAPSHOT +**Current Release**: 0.1.4 + **Current Snapshot**: 0.1.5-SNAPSHOT   **Gradle and Maven** `io.synadia:direct-batch` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 2da2e5c..3570da7 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.1.4" +def jarVersion = "0.1.5" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/encoded-kv/README.md b/encoded-kv/README.md index b12af3c..2ecbbf7 100644 --- a/encoded-kv/README.md +++ b/encoded-kv/README.md @@ -14,8 +14,8 @@ It requires a _codec_, which * decodes the encoded key back to the key object * decodes the encoded data bytes back into the value object. -**Current Release**: 0.0.3 -  **Current Snapshot**: 0.0.4-SNAPSHOT +**Current Release**: 0.0.4 +  **Current Snapshot**: 0.0.5-SNAPSHOT   **Gradle and Maven** `io.synadia:encoded-kv` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index 2861913..244ad56 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.4" +def jarVersion = "0.0.5" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/js-publish-extensions/README.md b/js-publish-extensions/README.md index 31f2969..38c5387 100644 --- a/js-publish-extensions/README.md +++ b/js-publish-extensions/README.md @@ -4,8 +4,8 @@ Extensions specific to JetStream publishing. -**Current Release**: 0.4.3 -  **Current Snapshot**: 0.4.3-SNAPSHOT +**Current Release**: 0.4.4 +  **Current Snapshot**: 0.4.5-SNAPSHOT   **Gradle and Maven** `io.synadia:jnats-js-publish-extensions` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index f2088a8..316630a 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.4.4" +def jarVersion = "0.4.5" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/request-many/README.md b/request-many/README.md index 4071390..ee03146 100644 --- a/request-many/README.md +++ b/request-many/README.md @@ -9,8 +9,8 @@ instead of the usual first response for a request. This allows you to implement * Scatter-gather pattern, getting responses from many workers. * Many responses, for instance, a multipart payload, from a single worker. -**Current Release**: 0.1.0 -  **Current Snapshot**: 0.1.1-SNAPSHOT +**Current Release**: 0.1.1 +  **Current Snapshot**: 0.1.2-SNAPSHOT   **Gradle and Maven** `io.synadia:request-many` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/request-many/build.gradle b/request-many/build.gradle index cf69219..be35fcd 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.1.1" +def jarVersion = "0.1.2" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/schedule-message/README.md b/schedule-message/README.md index 29037c8..daff4eb 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -17,7 +17,6 @@ https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) [![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) - ### Building a Scheduled Message A scheduled message is just a normal message with some extra headers. diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 210570c..152be51 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.0.2" +def jarVersion = "0.0.3" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From babf5f7f8c02979e51241d6f0d3f80fe8135d26a Mon Sep 17 00:00:00 2001 From: scottf Date: Sun, 5 Oct 2025 11:35:56 -0400 Subject: [PATCH 069/135] Batch Publish Async Commit --- .../BasicBatchPublishAsyncExample.java | 66 +++++++++++++++++++ .../examples/BasicBatchPublishExample.java | 20 +++++- .../io/synadia/bp/BatchPublishException.java | 21 +++++- .../java/io/synadia/bp/BatchPublisher.java | 39 ++++++++--- 4 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java diff --git a/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java new file mode 100644 index 0000000..38dbeb9 --- /dev/null +++ b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java @@ -0,0 +1,66 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.Nats; +import io.nats.client.api.PublishAck; +import io.nats.client.api.StreamConfiguration; +import io.synadia.bp.BatchPublishOptions; +import io.synadia.bp.BatchPublisher; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +public class BasicBatchPublishAsyncExample { + static final String NATS_URL = "nats://localhost:4222"; + static final String STREAM = "bpa-stream"; + static final String SUBJECT = "bpa-subject"; + static final String BATCH_ID = "bpa-batch-id"; + + public static void main(String[] args) throws Exception { + try (Connection nc = Nats.connect(NATS_URL)) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Set up a fresh counter stream + try { jsm.deleteStream(STREAM); } catch (JetStreamApiException ignore) {} + StreamConfiguration config = StreamConfiguration.builder() + .name(STREAM) + .subjects(SUBJECT) + .allowAtomicPublish() + .build(); + jsm.addStream(config); + + BatchPublisher publisher = BatchPublisher.builder() + .connection(nc) + .batchId(BATCH_ID) + .build(); + + publisher.add(SUBJECT, null); + CompletableFuture paf = publisher.commitAsync(SUBJECT, null); + PublishAck pa = paf.get(1, TimeUnit.SECONDS); + System.out.println("Batch [" + pa.getBatchId() + "] Committed " + pa.getJv()); + + publisher = BatchPublisher.builder() + .connection(nc) + .batchId(BATCH_ID + "-demonstrate-error") + .ackFirst(false) // otherwise error will happen on first publish + .build(); + + publisher.add(SUBJECT, null, BatchPublishOptions.builder().expectedLastSequence(1).build()); + paf = publisher.commitAsync(SUBJECT, null); + try { + // this will exception + paf.get(1, TimeUnit.SECONDS); + } + catch (ExecutionException e) { + //noinspection ThrowablePrintedToSystemOut + System.out.println(e); + } + } + } +} diff --git a/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java index ace04e8..704f0a2 100644 --- a/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java +++ b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java @@ -6,12 +6,15 @@ import io.nats.client.*; import io.nats.client.api.*; import io.nats.client.impl.Headers; +import io.synadia.bp.BatchPublishException; +import io.synadia.bp.BatchPublishOptions; import io.synadia.bp.BatchPublisher; public class BasicBatchPublishExample { static final String NATS_URL = "nats://localhost:4222"; static final String STREAM = "bp-stream"; static final String SUBJECT = "bp-subject"; + static final String BATCH_ID = "bp-batch-id"; static final int BATCH_SIZE = 1000; // !!! MAX IS 1000 static final boolean ACK_FIRST = true; // default is true usually never change this. static final int AUTO_ACK_EVERY = 100; // 0 or less means no auto ack @@ -33,7 +36,7 @@ public static void main(String[] args) throws Exception { BatchPublisher publisher = BatchPublisher.builder() .connection(nc) - .batchId(NUID.nextGlobal()) + .batchId(BATCH_ID) .ackFirst(ACK_FIRST) .ackEvery(AUTO_ACK_EVERY) .build(); @@ -70,6 +73,21 @@ public static void main(String[] args) throws Exception { m = sub.nextMessage(50); } System.out.println("Consumed " + count + " messages from '" + SUBJECT + "'"); + + publisher = BatchPublisher.builder() + .connection(nc) + .batchId(BATCH_ID + "-demonstrate-error") + .ackFirst(false) // otherwise error will happen on first publish + .build(); + publisher.add(SUBJECT, null, BatchPublishOptions.builder().expectedLastSequence(1).build()); + try { + // this will exception + publisher.commit(SUBJECT, null); + } + catch (BatchPublishException e) { + //noinspection ThrowablePrintedToSystemOut + System.out.println(e.getMessage()); + } } } diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java index 1329231..a311ddc 100644 --- a/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublishException.java @@ -4,26 +4,41 @@ package io.synadia.bp; import io.nats.client.JetStreamApiException; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; public class BatchPublishException extends Exception { private final JetStreamApiException jsApiException; + private final String batchId; - public BatchPublishException(String message) { + public BatchPublishException(@NonNull String batchId, @NonNull String message) { super(message); + this.batchId = batchId; jsApiException = null; } - public BatchPublishException(JetStreamApiException cause) { + public BatchPublishException(@NonNull String batchId, @NonNull JetStreamApiException cause) { super(cause); + this.batchId = batchId; jsApiException = cause; } - public BatchPublishException(Throwable cause) { + public BatchPublishException(@NonNull String batchId, @NonNull Throwable cause) { super(cause); + this.batchId = batchId; jsApiException = null; } + @Override + public String getMessage() { + return "[" + batchId + "] " + super.getMessage(); + } + + @NonNull + public String getBatchId() { + return batchId; + } + @Nullable public JetStreamApiException getJsApiException() { return jsApiException; diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java index d9278cf..3a13a96 100644 --- a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java @@ -112,7 +112,7 @@ public void add(String subject, Headers userHeaders, byte[] data) throws BatchPu public void add(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { if (state != State.Open) { - throw new BatchPublishException("Batch not open: " + state); + throw new BatchPublishException(batchId, "Batch not open: " + state); } if ( (++lastSeq == 1 && ackFirst) // first publish || (ackEvery > 0 && lastSeq % ackEvery == 0)) // or every publish @@ -139,7 +139,7 @@ public void addAcked(String subject, Headers userHeaders, byte[] data) throws Ba public void addAcked(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { if (state != State.Open) { - throw new BatchPublishException("Batch not open: " + state); + throw new BatchPublishException(batchId, "Batch not open: " + state); } ++lastSeq; _addAcked(subject, userHeaders, data, opts); @@ -148,7 +148,7 @@ public void addAcked(String subject, Headers userHeaders, byte[] data, BatchPubl private void _addAcked(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { Message m = request(subject, userHeaders, data, false, opts); if (m.getData().length != 0) { - throw new BatchPublishException("Invalid ack returned from add with confirm"); + throw new BatchPublishException(batchId, "Invalid ack returned from add with confirm"); } } @@ -166,7 +166,7 @@ public PublishAck commit(String subject, Headers userHeaders, byte[] data) throw public PublishAck commit(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { if (state != State.Open) { - throw new BatchPublishException("Batch not open: " + state); + throw new BatchPublishException(batchId, "Batch not open: " + state); } try { ++lastSeq; @@ -177,16 +177,39 @@ public PublishAck commit(String subject, Headers userHeaders, byte[] data, Batch // done this way because PublishAck makes an IOException if the ack is invalid. // it was done that way because of api backward compatibility // just no need of the extra layer - throw new BatchPublishException(e.getMessage()); + throw new BatchPublishException(batchId, e.getMessage()); } catch (JetStreamApiException e) { - throw new BatchPublishException(e); + throw new BatchPublishException(batchId, e); } finally { state = State.Closed; } } + public CompletableFuture commitAsync(String subject, byte[] data) throws BatchPublishException { + return commitAsync(subject, null, data, null); + } + + public CompletableFuture commitAsync(String subject, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + return commitAsync(subject, null, data, opts); + } + + public CompletableFuture commitAsync(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + return commitAsync(subject, userHeaders, data, null); + } + + public CompletableFuture commitAsync(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + return CompletableFuture.supplyAsync(() -> { + try { + return commit(subject, userHeaders, data, opts); + } + catch (BatchPublishException e) { + throw new RuntimeException(e); + } + }, conn.getOptions().getExecutor()); + } + private Message request(String subject, Headers userHeaders, byte[] data, boolean commit, BatchPublishOptions opts) throws BatchPublishException { try { updateHeaders(commit, userHeaders, opts); @@ -194,11 +217,11 @@ private Message request(String subject, Headers userHeaders, byte[] data, boolea return f.get(ackTimeout.toNanos(), TimeUnit.NANOSECONDS); } catch (ExecutionException | TimeoutException e) { - throw new BatchPublishException(e); + throw new BatchPublishException(batchId, e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new BatchPublishException(e); + throw new BatchPublishException(batchId, e); } } From d69d5a939b7ebe6cb27826ef0ba4a5a5feb1c797 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 6 Oct 2025 08:46:11 -0400 Subject: [PATCH 070/135] Batch Publish Async Commit --- .../java/io/synadia/examples/BasicBatchPublishAsyncExample.java | 2 +- .../java/io/synadia/examples/BasicBatchPublishExample.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java index 38dbeb9..8b1beae 100644 --- a/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java +++ b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishAsyncExample.java @@ -47,7 +47,7 @@ public static void main(String[] args) throws Exception { publisher = BatchPublisher.builder() .connection(nc) - .batchId(BATCH_ID + "-demonstrate-error") + .batchId(BATCH_ID + "-batch-error") .ackFirst(false) // otherwise error will happen on first publish .build(); diff --git a/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java index 704f0a2..8238f71 100644 --- a/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java +++ b/batch-publish/src/examples/java/io/synadia/examples/BasicBatchPublishExample.java @@ -76,7 +76,7 @@ public static void main(String[] args) throws Exception { publisher = BatchPublisher.builder() .connection(nc) - .batchId(BATCH_ID + "-demonstrate-error") + .batchId(BATCH_ID + "-batch-error") .ackFirst(false) // otherwise error will happen on first publish .build(); publisher.add(SUBJECT, null, BatchPublishOptions.builder().expectedLastSequence(1).build()); From dff1265dcdcc068bc4ceb1e00e82346eab16b2ac Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 6 Oct 2025 08:53:35 -0400 Subject: [PATCH 071/135] Batch Publish Async Commit Redux --- README.md | 4 ++-- batch-publish/README.md | 4 ++-- batch-publish/build.gradle | 2 +- .../src/main/java/io/synadia/bp/BatchPublisher.java | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 89b3c04..37b5d5e 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,8 @@ Run some NATS servers and cause chaos by bringing them up and down. Utility to publish an atomic batch, a group of up to 1000 messages -**Current Release**: 0.2.1 - **Current Snapshot**: 0.2.2-SNAPSHOT +**Current Release**: 0.2.2 + **Current Snapshot**: 0.2.3-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=batch-publish/README.md)](batch-publish/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-00BC8E?labelColor=grey&style=flat) diff --git a/batch-publish/README.md b/batch-publish/README.md index 992e2c1..4c9f403 100644 --- a/batch-publish/README.md +++ b/batch-publish/README.md @@ -11,8 +11,8 @@ Utility to publish an atomic batch, a group of up to 1000 messages https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-50.md -**Current Release**: 0.2.1 - **Current Snapshot**: 0.2.2-SNAPSHOT +**Current Release**: 0.2.2 + **Current Snapshot**: 0.2.3-SNAPSHOT   **Gradle and Maven** `io.synadia:batch-publish` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index 5287bde..806bfc8 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.2.2" +def jarVersion = "0.2.3" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" diff --git a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java index 3a13a96..80d6c3a 100644 --- a/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java +++ b/batch-publish/src/main/java/io/synadia/bp/BatchPublisher.java @@ -187,19 +187,19 @@ public PublishAck commit(String subject, Headers userHeaders, byte[] data, Batch } } - public CompletableFuture commitAsync(String subject, byte[] data) throws BatchPublishException { + public CompletableFuture commitAsync(String subject, byte[] data) { return commitAsync(subject, null, data, null); } - public CompletableFuture commitAsync(String subject, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + public CompletableFuture commitAsync(String subject, byte[] data, BatchPublishOptions opts) { return commitAsync(subject, null, data, opts); } - public CompletableFuture commitAsync(String subject, Headers userHeaders, byte[] data) throws BatchPublishException { + public CompletableFuture commitAsync(String subject, Headers userHeaders, byte[] data) { return commitAsync(subject, userHeaders, data, null); } - public CompletableFuture commitAsync(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) throws BatchPublishException { + public CompletableFuture commitAsync(String subject, Headers userHeaders, byte[] data, BatchPublishOptions opts) { return CompletableFuture.supplyAsync(() -> { try { return commit(subject, userHeaders, data, opts); From d5f685789229f0012b3893732dc50e8b14679793 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 7 Oct 2025 11:58:20 -0400 Subject: [PATCH 072/135] Added table to readme --- README.md | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 37b5d5e..c9e27bf 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,17 @@ boost productivity and provide a higher abstraction layer for the [JNATS](https: client. Note that these libraries will evolve rapidly and API guarantees are general not made until the specific project has a v1.0.0 version. # Utilities +| Module | Description | Docs | Release Version | Snapshot | +|--------------------------------------|------------------------------------------------------|-----------------------------------------------|-----------------|----------------| +| Retrier | Extension for retrying anything | [README.md](retrier/README.md) | 0.2.1 | 0.2.2-SNAPSHOT | +| Jetstream Publish Extensions | General extensions for Jetstream Publishing | [README.md](js-publish-extensions/README.md) | 0.4.4 | 0.4.5-SNAPSHOT | +| Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | +| Batch Publish | Publish an atomic batch | [README.md](dirrrrrrr/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | +| JetStream Distributed Counters CRDT | Leverage distributed counter functionality | [README.md](counter/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| JetStream Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.2 | 0.0.3-SNAPSHOT | +| Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | ## Retrier @@ -69,18 +80,6 @@ The functionality is described in [ADR-31](https://github.com/nats-io/nats-archi [![javadoc](https://javadoc.io/badge2/io.synadia/direct-batch/javadoc.svg)](https://javadoc.io/doc/io.synadia/direct-batch) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/direct-batch/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/direct-batch) -## Chaos Runner - -Run some NATS servers and cause chaos by bringing them up and down. - -**Current Release**: 0.0.8 -  **Current Snapshot**: 0.0.9-SNAPSHOT - -[![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](chaos-runner/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) -[![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner) - ### Batch Publish Utility to publish an atomic batch, a group of up to 1000 messages @@ -95,7 +94,7 @@ Utility to publish an atomic batch, a group of up to 1000 messages ### JetStream Distributed Counters CRDT -Utility to take advantage of the distributed counter functionality. +Utility to take leverage the distributed counter functionality. **Current Release**: 0.1.1   **Current Snapshot**: 0.1.2-SNAPSHOT @@ -118,6 +117,18 @@ Eventually the ability to schedule a message to publish based on a cron or sched [![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) +## Chaos Runner + +Run some NATS servers and cause chaos by bringing them up and down. + +**Current Release**: 0.0.8 +  **Current Snapshot**: 0.0.9-SNAPSHOT + +[![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](chaos-runner/README.md) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) +[![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner) + # Dependencies ### Gradle From d7784e1c4f30cedfdd103d70a202e22d843a9dab Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 7 Oct 2025 17:04:44 -0400 Subject: [PATCH 073/135] Project change Counter to Counters --- .../{counter-main.yml => counters-main.yml} | 8 +- .../{counter-pr.yml => counters-pr.yml} | 6 +- ...unter-release.yml => counters-release.yml} | 6 +- README.md | 12 +-- {counter => counters}/.gitignore | 0 {counter => counters}/LICENSE | 0 {counter => counters}/NOTICE | 0 {counter => counters}/README.md | 0 {counter => counters}/build.gradle | 6 +- .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 {counter => counters}/gradlew | 0 {counter => counters}/gradlew.bat | 0 {counter => counters}/settings.gradle | 2 +- .../io/synadia/examples/CounterExample.java | 96 +++++++++--------- .../io/synadia/counters}/CounterEntry.java | 8 +- .../counters}/CounterEntryResponse.java | 4 +- .../io/synadia/counters}/CounterIterator.java | 2 +- .../io/synadia/counters}/CounterResponse.java | 2 +- .../java/io/synadia/counters/Counters.java | 30 +++--- .../io/synadia/counters/CountersUtils.java | 6 +- .../src/main/javadoc/images/favicon.ico | Bin .../src/main/javadoc/images/large-logo.png | Bin .../src/main/javadoc/images/synadia-logo.png | Bin .../src/main/javadoc/overview.html | 0 .../src/main/resources/placeholder.txt | 0 .../io/synadia/counters/CountersTests.java | 96 +++++++++--------- .../src/test/resources/placeholder.txt | 0 {counter => counters}/test.bat | 0 29 files changed, 143 insertions(+), 141 deletions(-) rename .github/workflows/{counter-main.yml => counters-main.yml} (92%) rename .github/workflows/{counter-pr.yml => counters-pr.yml} (93%) rename .github/workflows/{counter-release.yml => counters-release.yml} (93%) rename {counter => counters}/.gitignore (100%) rename {counter => counters}/LICENSE (100%) rename {counter => counters}/NOTICE (100%) rename {counter => counters}/README.md (100%) rename {counter => counters}/build.gradle (97%) rename {counter => counters}/gradle/wrapper/gradle-wrapper.jar (100%) rename {counter => counters}/gradle/wrapper/gradle-wrapper.properties (100%) rename {counter => counters}/gradlew (100%) rename {counter => counters}/gradlew.bat (100%) rename {counter => counters}/settings.gradle (91%) rename {counter => counters}/src/examples/java/io/synadia/examples/CounterExample.java (80%) rename {counter/src/main/java/io/synadia/counter => counters/src/main/java/io/synadia/counters}/CounterEntry.java (89%) rename {counter/src/main/java/io/synadia/counter => counters/src/main/java/io/synadia/counters}/CounterEntryResponse.java (91%) rename {counter/src/main/java/io/synadia/counter => counters/src/main/java/io/synadia/counters}/CounterIterator.java (98%) rename {counter/src/main/java/io/synadia/counter => counters/src/main/java/io/synadia/counters}/CounterResponse.java (97%) rename counter/src/main/java/io/synadia/counter/Counter.java => counters/src/main/java/io/synadia/counters/Counters.java (86%) rename counter/src/main/java/io/synadia/counter/CounterUtils.java => counters/src/main/java/io/synadia/counters/CountersUtils.java (94%) rename {counter => counters}/src/main/javadoc/images/favicon.ico (100%) rename {counter => counters}/src/main/javadoc/images/large-logo.png (100%) rename {counter => counters}/src/main/javadoc/images/synadia-logo.png (100%) rename {counter => counters}/src/main/javadoc/overview.html (100%) rename {counter => counters}/src/main/resources/placeholder.txt (100%) rename counter/src/test/java/io/synadia/counter/CounterTests.java => counters/src/test/java/io/synadia/counters/CountersTests.java (63%) rename {counter => counters}/src/test/resources/placeholder.txt (100%) rename {counter => counters}/test.bat (100%) diff --git a/.github/workflows/counter-main.yml b/.github/workflows/counters-main.yml similarity index 92% rename from .github/workflows/counter-main.yml rename to .github/workflows/counters-main.yml index 02a7f0d..483dbc6 100644 --- a/.github/workflows/counter-main.yml +++ b/.github/workflows/counters-main.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - 'counter/**' + - 'counters/**' jobs: build: @@ -32,16 +32,16 @@ jobs: uses: actions/checkout@v3 - name: Compile and Test run: | - pushd counter + pushd counters chmod +x gradlew && ./gradlew clean test popd - name: Verify Javadoc run: | - pushd counter + pushd counters ./gradlew javadoc popd - name: Publish Snapshot run: | - pushd counter + pushd counters ./gradlew -i publishToSonatype popd diff --git a/.github/workflows/counter-pr.yml b/.github/workflows/counters-pr.yml similarity index 93% rename from .github/workflows/counter-pr.yml rename to .github/workflows/counters-pr.yml index bc2c9f1..2fe5625 100644 --- a/.github/workflows/counter-pr.yml +++ b/.github/workflows/counters-pr.yml @@ -4,7 +4,7 @@ on: pull_request: types: [opened, synchronize, reopened] paths: - - 'counter/**' + - 'counters/**' jobs: build: @@ -31,11 +31,11 @@ jobs: uses: actions/checkout@v3 - name: Compile and Test run: | - pushd counter + pushd counters chmod +x gradlew && ./gradlew clean test popd - name: Verify Javadoc run: | - pushd counter + pushd counters ./gradlew javadoc popd diff --git a/.github/workflows/counter-release.yml b/.github/workflows/counters-release.yml similarity index 93% rename from .github/workflows/counter-release.yml rename to .github/workflows/counters-release.yml index 912c303..f6659da 100644 --- a/.github/workflows/counter-release.yml +++ b/.github/workflows/counters-release.yml @@ -2,7 +2,7 @@ name: Counter Release on: push: - tags: [ 'counter/*' ] + tags: [ 'counters/*' ] jobs: build: @@ -29,11 +29,11 @@ jobs: uses: actions/checkout@v3 - name: Compile and Test run: | - pushd counter + pushd counters chmod +x gradlew && ./gradlew clean test popd - name: Verify, Sign and Publish Release run: | - pushd counter + pushd counters ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository popd diff --git a/README.md b/README.md index c9e27bf..6ee1a63 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ client. Note that these libraries will evolve rapidly and API guarantees are gen | Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | | Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | | Batch Publish | Publish an atomic batch | [README.md](dirrrrrrr/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | -| JetStream Distributed Counters CRDT | Leverage distributed counter functionality | [README.md](counter/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| JetStream Distributed Counters CRDT | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | | JetStream Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.2 | 0.0.3-SNAPSHOT | | Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | @@ -94,15 +94,15 @@ Utility to publish an atomic batch, a group of up to 1000 messages ### JetStream Distributed Counters CRDT -Utility to take leverage the distributed counter functionality. +Utility to take leverage the distributed counters functionality. **Current Release**: 0.1.1   **Current Snapshot**: 0.1.2-SNAPSHOT -[![README](https://img.shields.io/badge/README-blue?style=flat&link=counter/README.md)](counter/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counter-00BC8E?labelColor=grey&style=flat) -[![javadoc](https://javadoc.io/badge2/io.synadia/counter/javadoc.svg)](https://javadoc.io/doc/io.synadia/counter) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter) +[![README](https://img.shields.io/badge/README-blue?style=flat&link=counters/README.md)](counters/README.md) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-00BC8E?labelColor=grey&style=flat) +[![javadoc](https://javadoc.io/badge2/io.synadia/counters/javadoc.svg)](https://javadoc.io/doc/io.synadia/counters) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters) ### JetStream Scheduled Message diff --git a/counter/.gitignore b/counters/.gitignore similarity index 100% rename from counter/.gitignore rename to counters/.gitignore diff --git a/counter/LICENSE b/counters/LICENSE similarity index 100% rename from counter/LICENSE rename to counters/LICENSE diff --git a/counter/NOTICE b/counters/NOTICE similarity index 100% rename from counter/NOTICE rename to counters/NOTICE diff --git a/counter/README.md b/counters/README.md similarity index 100% rename from counter/README.md rename to counters/README.md diff --git a/counter/build.gradle b/counters/build.gradle similarity index 97% rename from counter/build.gradle rename to counters/build.gradle index 310e5cc..51cfde2 100644 --- a/counter/build.gradle +++ b/counters/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.1.2" +def jarVersion = "0.2.0" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" @@ -73,7 +73,7 @@ tasks.register('bundle', Bundle) { jar { manifest { - attributes('Automatic-Module-Name': 'io.synadia.counter') + attributes('Automatic-Module-Name': 'io.synadia.counters') } bnd (['Implementation-Title': 'JetStream Distributed Counters', 'Implementation-Version': jarVersion, @@ -171,7 +171,7 @@ publishing { artifact examplesJar artifact javadocJar pom { - name = 'counter' + name = 'counters' packaging = 'jar' groupId = group artifactId = archivesBaseName diff --git a/counter/gradle/wrapper/gradle-wrapper.jar b/counters/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from counter/gradle/wrapper/gradle-wrapper.jar rename to counters/gradle/wrapper/gradle-wrapper.jar diff --git a/counter/gradle/wrapper/gradle-wrapper.properties b/counters/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from counter/gradle/wrapper/gradle-wrapper.properties rename to counters/gradle/wrapper/gradle-wrapper.properties diff --git a/counter/gradlew b/counters/gradlew similarity index 100% rename from counter/gradlew rename to counters/gradlew diff --git a/counter/gradlew.bat b/counters/gradlew.bat similarity index 100% rename from counter/gradlew.bat rename to counters/gradlew.bat diff --git a/counter/settings.gradle b/counters/settings.gradle similarity index 91% rename from counter/settings.gradle rename to counters/settings.gradle index 59f0439..46d185c 100644 --- a/counter/settings.gradle +++ b/counters/settings.gradle @@ -7,4 +7,4 @@ * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html */ -rootProject.name = 'counter' +rootProject.name = 'counters' diff --git a/counter/src/examples/java/io/synadia/examples/CounterExample.java b/counters/src/examples/java/io/synadia/examples/CounterExample.java similarity index 80% rename from counter/src/examples/java/io/synadia/examples/CounterExample.java rename to counters/src/examples/java/io/synadia/examples/CounterExample.java index 8d24a28..43c6679 100644 --- a/counter/src/examples/java/io/synadia/examples/CounterExample.java +++ b/counters/src/examples/java/io/synadia/examples/CounterExample.java @@ -9,9 +9,9 @@ import io.nats.client.Nats; import io.nats.client.api.StorageType; import io.nats.client.api.StreamConfiguration; -import io.synadia.counter.Counter; -import io.synadia.counter.CounterEntryResponse; -import io.synadia.counter.CounterIterator; +import io.synadia.counters.CounterEntryResponse; +import io.synadia.counters.CounterIterator; +import io.synadia.counters.Counters; import java.math.BigInteger; import java.time.Duration; @@ -19,6 +19,8 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import static io.synadia.counters.Counters.createCountersStream; + public class CounterExample { static final String NATS_URL = "nats://localhost:4222"; @@ -26,41 +28,41 @@ public static void main(String[] args) throws Exception { try (Connection nc = Nats.connect(NATS_URL)) { JetStreamManagement jsm = nc.jetStreamManagement(); - // Set up a fresh counter stream - try { jsm.deleteStream("counter-stream"); } catch (JetStreamApiException ignore) {} - Counter counter = Counter.createCounterStream(nc, + // Set up a fresh counters stream + try { jsm.deleteStream("counters-stream"); } catch (JetStreamApiException ignore) {} + Counters counters = createCountersStream(nc, StreamConfiguration.builder() - .name("counter-stream") + .name("counters-stream") .subjects("cs.*") .storageType(StorageType.Memory) .build()); // ---------------------------------------------------------------------------------------------------- System.out.println("1.1: Add to a subject..."); - System.out.println(" add(\"cs.A\", 1) -> " + counter.add("cs.A", 1)); - System.out.println(" add(\"cs.A\", 2) -> " + counter.add("cs.A", 2)); - System.out.println(" add(\"cs.A\", 3) -> " + counter.add("cs.A", 3)); - System.out.println(" add(\"cs.A\", -1) -> " + counter.add("cs.A", -1)); + System.out.println(" add(\"cs.A\", 1) -> " + counters.add("cs.A", 1)); + System.out.println(" add(\"cs.A\", 2) -> " + counters.add("cs.A", 2)); + System.out.println(" add(\"cs.A\", 3) -> " + counters.add("cs.A", 3)); + System.out.println(" add(\"cs.A\", -1) -> " + counters.add("cs.A", -1)); - System.out.println(" add(\"cs.B\", 10) -> " + counter.add("cs.B", 10)); - System.out.println(" add(\"cs.B\", 20) -> " + counter.add("cs.B", 20)); - System.out.println(" add(\"cs.B\", 30) -> " + counter.add("cs.B", 30)); - System.out.println(" add(\"cs.B\", -10) -> " + counter.add("cs.B", -10)); + System.out.println(" add(\"cs.B\", 10) -> " + counters.add("cs.B", 10)); + System.out.println(" add(\"cs.B\", 20) -> " + counters.add("cs.B", 20)); + System.out.println(" add(\"cs.B\", 30) -> " + counters.add("cs.B", 30)); + System.out.println(" add(\"cs.B\", -10) -> " + counters.add("cs.B", -10)); - System.out.println(" add(\"cs.C\", 100) -> " + counter.add("cs.C", 100)); - System.out.println(" add(\"cs.C\", 200) -> " + counter.add("cs.C", 200)); - System.out.println(" add(\"cs.C\", 300) -> " + counter.add("cs.C", 300)); - System.out.println(" add(\"cs.C\", -100) -> " + counter.add("cs.C", -100)); + System.out.println(" add(\"cs.C\", 100) -> " + counters.add("cs.C", 100)); + System.out.println(" add(\"cs.C\", 200) -> " + counters.add("cs.C", 200)); + System.out.println(" add(\"cs.C\", 300) -> " + counters.add("cs.C", 300)); + System.out.println(" add(\"cs.C\", -100) -> " + counters.add("cs.C", -100)); // ---------------------------------------------------------------------------------------------------- System.out.println("\n2.1: get() for existing subjects"); - System.out.println(" get(\"cs.A\") -> " + counter.get("cs.A")); - System.out.println(" get(\"cs.B\") -> " + counter.get("cs.B")); - System.out.println(" get(\"cs.C\") -> " + counter.get("cs.C")); + System.out.println(" get(\"cs.A\") -> " + counters.get("cs.A")); + System.out.println(" get(\"cs.B\") -> " + counters.get("cs.B")); + System.out.println(" get(\"cs.C\") -> " + counters.get("cs.C")); System.out.println("\n2.2: get() when the subject is not found"); try { - counter.get("cs.not-found"); + counters.get("cs.not-found"); } catch (JetStreamApiException e) { System.out.println(" get(\"cs.not-found\") -> " + e); @@ -68,33 +70,33 @@ public static void main(String[] args) throws Exception { System.out.println("\n2.3: get() for a single subject does not allow wildcards"); try { - counter.get("cs.*"); + counters.get("cs.*"); } catch (IllegalArgumentException e) { System.out.println(" get(\"cs.*\") -> " + e); } System.out.println("\n2.4: getOrElse() with a default when the subject is found"); - System.out.println(" getOrElse(\"cs.C\", BigInteger.ZERO\") -> " + counter.getOrElse("cs.C", BigInteger.ZERO)); + System.out.println(" getOrElse(\"cs.C\", BigInteger.ZERO\") -> " + counters.getOrElse("cs.C", BigInteger.ZERO)); System.out.println("\n2.5: getOrElse() with a default when the subject is not found"); try { - counter.get("cs.not-found"); + counters.get("cs.not-found"); } catch (JetStreamApiException e) { System.out.println(" get(\"cs.not-found\") -> " + e); } - System.out.println(" getOrElse(\"cs.not-found\", 77777) -> " + counter.getOrElse("cs.not-found", 77777)); + System.out.println(" getOrElse(\"cs.not-found\", 77777) -> " + counters.getOrElse("cs.not-found", 77777)); // ---------------------------------------------------------------------------------------------------- System.out.println("\n3.1: getEntry() - The full CounterEntry for a subject, notice the last increment..."); - System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); - System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); - System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); + System.out.println(" getEntry(\"cs.A\") -> " + counters.getEntry("cs.A")); + System.out.println(" getEntry(\"cs.B\") -> " + counters.getEntry("cs.B")); + System.out.println(" getEntry(\"cs.C\") -> " + counters.getEntry("cs.C")); System.out.println("\n3.2: getEntry() does not allow wildcards"); try { - counter.getEntry("cs.>"); + counters.getEntry("cs.>"); } catch (IllegalArgumentException e) { System.out.println(" getEntry(\"cs.>\") -> " + e); @@ -102,7 +104,7 @@ public static void main(String[] args) throws Exception { // ---------------------------------------------------------------------------------------------------- System.out.println("\n4.1: getEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get the CounterEntryResponse objects for multiple subjects."); - LinkedBlockingQueue eResponses = counter.getEntries("cs.A", "cs.B", "cs.C"); + LinkedBlockingQueue eResponses = counters.getEntries("cs.A", "cs.B", "cs.C"); BigInteger total = BigInteger.ZERO; CounterEntryResponse er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { @@ -115,7 +117,7 @@ public static void main(String[] args) throws Exception { System.out.println(" Values totaled: " + total); System.out.println("\n4.2: getEntries(\"cs.*\") - Get CounterEntryResponse objects for wildcard subject(s)."); - eResponses = counter.getEntries("cs.*"); + eResponses = counters.getEntries("cs.*"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { System.out.println(" " + er); @@ -127,29 +129,29 @@ public static void main(String[] args) throws Exception { System.out.println("\n5.1: setViaAdd() - Sets the value for a subject by" + "\n 1) calling getOrElse(subject, BigInteger.ZERO)" + "\n 2) then calling add with the set value minus the current value."); - System.out.println(" setViaAdd(\"cs.A\", 9) -> " + counter.setViaAdd("cs.A", 9)); - System.out.println(" setViaAdd(\"cs.B\", 99) -> " + counter.setViaAdd("cs.B", 99)); - System.out.println(" setViaAdd(\"cs.C\", 999) -> " + counter.setViaAdd("cs.C", 999)); + System.out.println(" setViaAdd(\"cs.A\", 9) -> " + counters.setViaAdd("cs.A", 9)); + System.out.println(" setViaAdd(\"cs.B\", 99) -> " + counters.setViaAdd("cs.B", 99)); + System.out.println(" setViaAdd(\"cs.C\", 999) -> " + counters.setViaAdd("cs.C", 999)); System.out.println("\n5.2: getEntry() - Get the full CounterEntry, notice the last increment after a setViaAdd" + "\n represents the difference between the entry before the set and the set value."); - System.out.println(" getEntry(\"cs.A\") -> " + counter.getEntry("cs.A")); - System.out.println(" getEntry(\"cs.B\") -> " + counter.getEntry("cs.B")); - System.out.println(" getEntry(\"cs.C\") -> " + counter.getEntry("cs.C")); + System.out.println(" getEntry(\"cs.A\") -> " + counters.getEntry("cs.A")); + System.out.println(" getEntry(\"cs.B\") -> " + counters.getEntry("cs.B")); + System.out.println(" getEntry(\"cs.C\") -> " + counters.getEntry("cs.C")); System.out.println("\n5.3: It's safe to call setViaAdd() even if the subject did not exist because it uses getOrElse;"); try { - counter.get("cs.did-not-exist"); + counters.get("cs.did-not-exist"); } catch (JetStreamApiException e) { System.out.println(" get(\"cs.did-not-exist\") -> " + e); } - System.out.println(" setViaAdd(\"cs.did-not-exist\", 99999) -> " + counter.setViaAdd("cs.did-not-exist", 99999)); - System.out.println(" get(\"cs.did-not-exist\") -> " + counter.get("cs.did-not-exist")); + System.out.println(" setViaAdd(\"cs.did-not-exist\", 99999) -> " + counters.setViaAdd("cs.did-not-exist", 99999)); + System.out.println(" get(\"cs.did-not-exist\") -> " + counters.get("cs.did-not-exist")); // ---------------------------------------------------------------------------------------------------- System.out.println("\n6.1: getEntries(\"cs.no-counters\", \"cs.also-counters\") - getEntries but no subjects have counters."); - eResponses = counter.getEntries("cs.no-counters", "cs.also-counters"); + eResponses = counters.getEntries("cs.no-counters", "cs.also-counters"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { System.out.println(" " + er); @@ -159,7 +161,7 @@ public static void main(String[] args) throws Exception { // ---------------------------------------------------------------------------------------------------- System.out.println("\n7.1: getEntries(\"no-counters\", \"cs.A\", \"cs.B\", \"cs.C\") - getEntries when some subjects have counters."); - eResponses = counter.getEntries("cs.no-counters", "cs.A", "cs.B", "cs.C"); + eResponses = counters.getEntries("cs.no-counters", "cs.A", "cs.B", "cs.C"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { System.out.println(" " + er); @@ -169,13 +171,13 @@ public static void main(String[] args) throws Exception { // ---------------------------------------------------------------------------------------------------- System.out.println("\n8.1: iterateEntries(\"cs.A\", \"cs.B\", \"cs.C\") - Get via CounterIterator for multiple subjects."); - CounterIterator iterator = counter.iterateEntries("cs.A", "cs.B", "cs.C"); + CounterIterator iterator = counters.iterateEntries("cs.A", "cs.B", "cs.C"); while (iterator.hasNext()) { System.out.println(" " + iterator.next()); } System.out.println("\n8.2: iterateEntries(\"cs.*\") - Get via CounterIterator for wildcard subject(s)."); - iterator = counter.iterateEntries("cs.*"); + iterator = counters.iterateEntries("cs.*"); while (iterator.hasNext()) { System.out.println(" " + iterator.next()); } @@ -183,7 +185,7 @@ public static void main(String[] args) throws Exception { System.out.println("\n8.3: iterateEntries(\"cs.*\", timeoutFirst, timeoutSubsequent) - Get via CounterIterator with custom timeouts."); Duration timeoutFirst = Duration.ofMillis(1000); Duration timeoutSubsequent = Duration.ofMillis(200); - iterator = counter.iterateEntries(Collections.singletonList("cs.*"), timeoutFirst, timeoutSubsequent); + iterator = counters.iterateEntries(Collections.singletonList("cs.*"), timeoutFirst, timeoutSubsequent); while (iterator.hasNext()) { System.out.println(" " + iterator.next()); } diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counters/src/main/java/io/synadia/counters/CounterEntry.java similarity index 89% rename from counter/src/main/java/io/synadia/counter/CounterEntry.java rename to counters/src/main/java/io/synadia/counters/CounterEntry.java index bd4804b..5880a2d 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntry.java +++ b/counters/src/main/java/io/synadia/counters/CounterEntry.java @@ -1,7 +1,7 @@ // Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. -package io.synadia.counter; +package io.synadia.counters; import io.nats.client.api.MessageInfo; import io.nats.client.impl.Headers; @@ -10,7 +10,7 @@ import java.math.BigInteger; import java.util.Map; -import static io.synadia.counter.CounterUtils.*; +import static io.synadia.counters.CountersUtils.*; public class CounterEntry { private final String subject; @@ -36,13 +36,13 @@ public class CounterEntry { throw invalidCounterMessage(null); } - String temp = h.getFirst(CounterUtils.INCREMENT_HEADER); + String temp = h.getFirst(CountersUtils.INCREMENT_HEADER); if (temp == null) { throw invalidCounterMessage(null); } lastIncrement = extractLastIncrement(temp); - sources = extractSources(h.getFirst(CounterUtils.SOURCES_HEADER)); + sources = extractSources(h.getFirst(CountersUtils.SOURCES_HEADER)); } private static RuntimeException invalidCounterMessage(Exception e) { diff --git a/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java b/counters/src/main/java/io/synadia/counters/CounterEntryResponse.java similarity index 91% rename from counter/src/main/java/io/synadia/counter/CounterEntryResponse.java rename to counters/src/main/java/io/synadia/counters/CounterEntryResponse.java index c72a6fd..238d557 100644 --- a/counter/src/main/java/io/synadia/counter/CounterEntryResponse.java +++ b/counters/src/main/java/io/synadia/counters/CounterEntryResponse.java @@ -1,14 +1,14 @@ // Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. -package io.synadia.counter; +package io.synadia.counters; import io.nats.client.api.MessageInfo; import org.jspecify.annotations.Nullable; import java.math.BigInteger; -import static io.synadia.counter.CounterUtils.extractVal; +import static io.synadia.counters.CountersUtils.extractVal; public class CounterEntryResponse extends CounterResponse { diff --git a/counter/src/main/java/io/synadia/counter/CounterIterator.java b/counters/src/main/java/io/synadia/counters/CounterIterator.java similarity index 98% rename from counter/src/main/java/io/synadia/counter/CounterIterator.java rename to counters/src/main/java/io/synadia/counters/CounterIterator.java index fb9350d..cc4c522 100644 --- a/counter/src/main/java/io/synadia/counter/CounterIterator.java +++ b/counters/src/main/java/io/synadia/counters/CounterIterator.java @@ -1,7 +1,7 @@ // Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. -package io.synadia.counter; +package io.synadia.counters; import java.time.Duration; import java.util.Iterator; diff --git a/counter/src/main/java/io/synadia/counter/CounterResponse.java b/counters/src/main/java/io/synadia/counters/CounterResponse.java similarity index 97% rename from counter/src/main/java/io/synadia/counter/CounterResponse.java rename to counters/src/main/java/io/synadia/counters/CounterResponse.java index 8310153..5811fce 100644 --- a/counter/src/main/java/io/synadia/counter/CounterResponse.java +++ b/counters/src/main/java/io/synadia/counters/CounterResponse.java @@ -1,7 +1,7 @@ // Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. -package io.synadia.counter; +package io.synadia.counters; import io.nats.client.api.MessageInfo; import io.nats.client.support.Status; diff --git a/counter/src/main/java/io/synadia/counter/Counter.java b/counters/src/main/java/io/synadia/counters/Counters.java similarity index 86% rename from counter/src/main/java/io/synadia/counter/Counter.java rename to counters/src/main/java/io/synadia/counters/Counters.java index 6cf697e..ef450c6 100644 --- a/counter/src/main/java/io/synadia/counter/Counter.java +++ b/counters/src/main/java/io/synadia/counters/Counters.java @@ -1,7 +1,7 @@ // Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. -package io.synadia.counter; +package io.synadia.counters; import io.nats.client.*; import io.nats.client.api.*; @@ -19,16 +19,16 @@ import java.util.concurrent.LinkedBlockingQueue; import static io.nats.client.support.Validator.required; -import static io.synadia.counter.CounterUtils.INCREMENT_HEADER; -import static io.synadia.counter.CounterUtils.extractVal; +import static io.synadia.counters.CountersUtils.INCREMENT_HEADER; +import static io.synadia.counters.CountersUtils.extractVal; -public class Counter { +public class Counters { - public static Counter createCounterStream(Connection conn, StreamConfiguration userConfig) throws JetStreamApiException, IOException { - return createCounterStream(conn, null, userConfig); + public static Counters createCountersStream(Connection conn, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + return createCountersStream(conn, null, userConfig); } - public static Counter createCounterStream(Connection conn, JetStreamOptions jso, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + public static Counters createCountersStream(Connection conn, JetStreamOptions jso, StreamConfiguration userConfig) throws JetStreamApiException, IOException { if (userConfig.getRetentionPolicy() != RetentionPolicy.Limits) { throw new IllegalArgumentException("Retention Policy - Limits is the only allowed limit for counter streams."); } @@ -43,7 +43,7 @@ public static Counter createCounterStream(Connection conn, JetStreamOptions jso, JetStreamManagement jsm = conn.jetStreamManagement(jso); StreamInfo si = jsm.addStream(config); - return new Counter(config.getName(), conn, jso, jsm, si); + return new Counters(config.getName(), conn, jso, jsm, si); } private final String streamName; @@ -53,19 +53,19 @@ public static Counter createCounterStream(Connection conn, JetStreamOptions jso, private final JetStream js; private final DirectBatchContext dbCtx; - public Counter(String streamName, Connection conn) throws IOException, JetStreamApiException { + public Counters(String streamName, Connection conn) throws IOException, JetStreamApiException { this(streamName, conn, null, null, null); } - public Counter(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException { + public Counters(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException { this(streamName, conn, jso, null, null); } - private Counter(@NonNull String streamName, - @NonNull Connection conn, - @Nullable JetStreamOptions jso, - @Nullable JetStreamManagement jsm, - @Nullable StreamInfo si + private Counters(@NonNull String streamName, + @NonNull Connection conn, + @Nullable JetStreamOptions jso, + @Nullable JetStreamManagement jsm, + @Nullable StreamInfo si ) throws IOException, JetStreamApiException { this.conn = conn; diff --git a/counter/src/main/java/io/synadia/counter/CounterUtils.java b/counters/src/main/java/io/synadia/counters/CountersUtils.java similarity index 94% rename from counter/src/main/java/io/synadia/counter/CounterUtils.java rename to counters/src/main/java/io/synadia/counters/CountersUtils.java index 1db9b07..3ca0514 100644 --- a/counter/src/main/java/io/synadia/counter/CounterUtils.java +++ b/counters/src/main/java/io/synadia/counters/CountersUtils.java @@ -1,7 +1,7 @@ // Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. -package io.synadia.counter; +package io.synadia.counters; import io.nats.client.support.JsonParseException; import io.nats.client.support.JsonParser; @@ -13,10 +13,10 @@ import java.util.HashMap; import java.util.Map; -public final class CounterUtils { +public final class CountersUtils { public static final String INCREMENT_HEADER = "Nats-Incr"; - public static final String SOURCES_HEADER = "Nats-Counter-Sources"; + public static final String SOURCES_HEADER = "Nats-Counters-Sources"; public static BigInteger extractVal(byte @NonNull [] valBytes) { String s = new String(valBytes); diff --git a/counter/src/main/javadoc/images/favicon.ico b/counters/src/main/javadoc/images/favicon.ico similarity index 100% rename from counter/src/main/javadoc/images/favicon.ico rename to counters/src/main/javadoc/images/favicon.ico diff --git a/counter/src/main/javadoc/images/large-logo.png b/counters/src/main/javadoc/images/large-logo.png similarity index 100% rename from counter/src/main/javadoc/images/large-logo.png rename to counters/src/main/javadoc/images/large-logo.png diff --git a/counter/src/main/javadoc/images/synadia-logo.png b/counters/src/main/javadoc/images/synadia-logo.png similarity index 100% rename from counter/src/main/javadoc/images/synadia-logo.png rename to counters/src/main/javadoc/images/synadia-logo.png diff --git a/counter/src/main/javadoc/overview.html b/counters/src/main/javadoc/overview.html similarity index 100% rename from counter/src/main/javadoc/overview.html rename to counters/src/main/javadoc/overview.html diff --git a/counter/src/main/resources/placeholder.txt b/counters/src/main/resources/placeholder.txt similarity index 100% rename from counter/src/main/resources/placeholder.txt rename to counters/src/main/resources/placeholder.txt diff --git a/counter/src/test/java/io/synadia/counter/CounterTests.java b/counters/src/test/java/io/synadia/counters/CountersTests.java similarity index 63% rename from counter/src/test/java/io/synadia/counter/CounterTests.java rename to counters/src/test/java/io/synadia/counters/CountersTests.java index 724d8ae..c916974 100644 --- a/counter/src/test/java/io/synadia/counter/CounterTests.java +++ b/counters/src/test/java/io/synadia/counters/CountersTests.java @@ -1,4 +1,4 @@ -package io.synadia.counter; +package io.synadia.counters; import io.nats.client.*; import io.nats.client.api.StorageType; @@ -15,11 +15,11 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import static io.synadia.counter.CounterUtils.extractSources; -import static io.synadia.counter.CounterUtils.extractVal; +import static io.synadia.counters.CountersUtils.extractSources; +import static io.synadia.counters.CountersUtils.extractVal; import static org.junit.jupiter.api.Assertions.*; -public class CounterTests { +public class CountersTests { static NatsServerRunner runner; static Connection nc; static JetStreamManagement jsm; @@ -43,8 +43,8 @@ public static void afterAll() throws Exception { runner.close(); } - private static Counter createCounterStream(String streamName, String... subjects) throws JetStreamApiException, IOException { - return Counter.createCounterStream(nc, + private static Counters createCountersStream(String streamName, String... subjects) throws JetStreamApiException, IOException { + return Counters.createCountersStream(nc, StreamConfiguration.builder() .name(streamName) .subjects(subjects) @@ -58,9 +58,9 @@ public void testCounterExceptions() { String subjectPrefix = NUID.nextGlobalSequence(); String wild = subjectPrefix + ".*"; try { - Counter counter = createCounterStream(streamName, wild); - assertThrows(IllegalArgumentException.class, () -> counter.add(wild, 1)); - assertThrows(IllegalArgumentException.class, () -> counter.get(wild)); + Counters counters = createCountersStream(streamName, wild); + assertThrows(IllegalArgumentException.class, () -> counters.add(wild, 1)); + assertThrows(IllegalArgumentException.class, () -> counters.get(wild)); String streamNameX = NUID.nextGlobalSequence(); String subjectPrefixX = NUID.nextGlobalSequence(); @@ -70,7 +70,7 @@ public void testCounterExceptions() { .subjects(wildX) .storageType(StorageType.Memory) .build()); - assertThrows(IllegalArgumentException.class, () -> new Counter(streamNameX, nc)); + assertThrows(IllegalArgumentException.class, () -> new Counters(streamNameX, nc)); } catch (JetStreamApiException | IOException e) { fail(e.getMessage()); @@ -81,50 +81,50 @@ public void testCounterExceptions() { public void testCounterBasics() throws Exception { String streamName = NUID.nextGlobalSequence(); String subjectPrefix = NUID.nextGlobalSequence(); - Counter counter = createCounterStream(streamName, subjectPrefix + ".*"); + Counters counters = createCountersStream(streamName, subjectPrefix + ".*"); String subject1 = subjectPrefix + "." + NUID.nextGlobalSequence(); String subject2 = subjectPrefix + "." + NUID.nextGlobalSequence(); String subject3 = subjectPrefix + "." + NUID.nextGlobalSequence(); - assertEquals(1, counter.add(subject1, 1).intValue()); - assertEquals(1, counter.get(subject1).intValue()); - assertEquals(3, counter.add(subject1, 2).intValue()); - assertEquals(3, counter.get(subject1).intValue()); - assertEquals(6, counter.add(subject1, 3).intValue()); - assertEquals(6, counter.get(subject1).intValue()); - assertEquals(5, counter.add(subject1, -1).intValue()); - assertEquals(5, counter.get(subject1).intValue()); - assertEquals(6, counter.increment(subject1).intValue()); - assertEquals(5, counter.decrement(subject1).intValue()); - - assertEquals(5, counter.getOrElse(subject1, 99).intValue()); - assertEquals(Integer.MAX_VALUE, counter.getOrElse("not-exist", Integer.MAX_VALUE).intValue()); - assertEquals(Long.MAX_VALUE, counter.getOrElse("not-exist", Long.MAX_VALUE).longValue()); - - assertEquals(-1, counter.add(subject2, -1).intValue()); - assertEquals(-1, counter.get(subject2).intValue()); - assertEquals(-3, counter.add(subject2, -2).intValue()); - assertEquals(-3, counter.get(subject2).intValue()); - assertEquals(-6, counter.add(subject2, -3).intValue()); - assertEquals(-6, counter.get(subject2).intValue()); - assertEquals(-5, counter.add(subject2, 1).intValue()); - assertEquals(-5, counter.get(subject2).intValue()); - - assertEquals(Integer.MAX_VALUE, counter.setViaAdd(subject3, Integer.MAX_VALUE).intValue()); - assertEquals(Integer.MAX_VALUE, counter.get(subject3).intValue()); - assertEquals(Long.MAX_VALUE, counter.setViaAdd(subject3, Long.MAX_VALUE).longValue()); - assertEquals(Long.MAX_VALUE, counter.get(subject3).longValue()); - - assertEquals(10, counter.setViaAdd(subject1, 10).intValue()); - assertEquals(100, counter.setViaAdd(subject2, 100).intValue()); - assertEquals(1000, counter.setViaAdd(subject3, 1000).intValue()); - assertEquals(10, counter.get(subject1).intValue()); - assertEquals(100, counter.get(subject2).intValue()); - assertEquals(1000, counter.get(subject3).intValue()); + assertEquals(1, counters.add(subject1, 1).intValue()); + assertEquals(1, counters.get(subject1).intValue()); + assertEquals(3, counters.add(subject1, 2).intValue()); + assertEquals(3, counters.get(subject1).intValue()); + assertEquals(6, counters.add(subject1, 3).intValue()); + assertEquals(6, counters.get(subject1).intValue()); + assertEquals(5, counters.add(subject1, -1).intValue()); + assertEquals(5, counters.get(subject1).intValue()); + assertEquals(6, counters.increment(subject1).intValue()); + assertEquals(5, counters.decrement(subject1).intValue()); + + assertEquals(5, counters.getOrElse(subject1, 99).intValue()); + assertEquals(Integer.MAX_VALUE, counters.getOrElse("not-exist", Integer.MAX_VALUE).intValue()); + assertEquals(Long.MAX_VALUE, counters.getOrElse("not-exist", Long.MAX_VALUE).longValue()); + + assertEquals(-1, counters.add(subject2, -1).intValue()); + assertEquals(-1, counters.get(subject2).intValue()); + assertEquals(-3, counters.add(subject2, -2).intValue()); + assertEquals(-3, counters.get(subject2).intValue()); + assertEquals(-6, counters.add(subject2, -3).intValue()); + assertEquals(-6, counters.get(subject2).intValue()); + assertEquals(-5, counters.add(subject2, 1).intValue()); + assertEquals(-5, counters.get(subject2).intValue()); + + assertEquals(Integer.MAX_VALUE, counters.setViaAdd(subject3, Integer.MAX_VALUE).intValue()); + assertEquals(Integer.MAX_VALUE, counters.get(subject3).intValue()); + assertEquals(Long.MAX_VALUE, counters.setViaAdd(subject3, Long.MAX_VALUE).longValue()); + assertEquals(Long.MAX_VALUE, counters.get(subject3).longValue()); + + assertEquals(10, counters.setViaAdd(subject1, 10).intValue()); + assertEquals(100, counters.setViaAdd(subject2, 100).intValue()); + assertEquals(1000, counters.setViaAdd(subject3, 1000).intValue()); + assertEquals(10, counters.get(subject1).intValue()); + assertEquals(100, counters.get(subject2).intValue()); + assertEquals(1000, counters.get(subject3).intValue()); BigInteger total = BigInteger.ZERO; - LinkedBlockingQueue eResponses = counter.getEntries(subject1, subject2, subject3); + LinkedBlockingQueue eResponses = counters.getEntries(subject1, subject2, subject3); CounterEntryResponse er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { CounterEntry entry = er.getEntry(); @@ -137,7 +137,7 @@ public void testCounterBasics() throws Exception { assertEquals(1110, total.intValue()); total = BigInteger.ZERO; - eResponses = counter.getEntries(subjectPrefix + ".*"); + eResponses = counters.getEntries(subjectPrefix + ".*"); er = eResponses.poll(1, TimeUnit.SECONDS); while (er != null && er.isEntry()) { CounterEntry entry = er.getEntry(); diff --git a/counter/src/test/resources/placeholder.txt b/counters/src/test/resources/placeholder.txt similarity index 100% rename from counter/src/test/resources/placeholder.txt rename to counters/src/test/resources/placeholder.txt diff --git a/counter/test.bat b/counters/test.bat similarity index 100% rename from counter/test.bat rename to counters/test.bat From 91debc58fa4da429631bd637d87eb800db855d58 Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 8 Oct 2025 10:52:47 -0400 Subject: [PATCH 074/135] Update readme --- README.md | 32 ++++++++++++++++---------------- counters/README.md | 14 +++++++------- counters/build.gradle | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 6ee1a63..5abe33d 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,17 @@ boost productivity and provide a higher abstraction layer for the [JNATS](https: client. Note that these libraries will evolve rapidly and API guarantees are general not made until the specific project has a v1.0.0 version. # Utilities -| Module | Description | Docs | Release Version | Snapshot | -|--------------------------------------|------------------------------------------------------|-----------------------------------------------|-----------------|----------------| -| Retrier | Extension for retrying anything | [README.md](retrier/README.md) | 0.2.1 | 0.2.2-SNAPSHOT | -| Jetstream Publish Extensions | General extensions for Jetstream Publishing | [README.md](js-publish-extensions/README.md) | 0.4.4 | 0.4.5-SNAPSHOT | -| Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | -| Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | -| Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | -| Batch Publish | Publish an atomic batch | [README.md](dirrrrrrr/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | -| JetStream Distributed Counters CRDT | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | -| JetStream Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.2 | 0.0.3-SNAPSHOT | -| Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | +| Module | Description | Docs | Release Version | Snapshot | +|------------------------------|------------------------------------------------------|-----------------------------------------------|-----------------|----------------| +| Retrier | Extension for retrying anything | [README.md](retrier/README.md) | 0.2.1 | 0.2.2-SNAPSHOT | +| Jetstream Publish Extensions | General extensions for Jetstream Publishing | [README.md](js-publish-extensions/README.md) | 0.4.4 | 0.4.5-SNAPSHOT | +| Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | +| Batch Publish | Publish an atomic batch | [README.md](dirrrrrrr/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | +| Distributed Counters | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.2.0 | 0.2.1-SNAPSHOT | +| Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.2 | 0.0.3-SNAPSHOT | +| Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | ## Retrier @@ -92,19 +92,19 @@ Utility to publish an atomic batch, a group of up to 1000 messages [![javadoc](https://javadoc.io/badge2/io.synadia/batch-publish/javadoc.svg)](https://javadoc.io/doc/io.synadia/batch-publish) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish) -### JetStream Distributed Counters CRDT +### Distributed Counters CRDT -Utility to take leverage the distributed counters functionality. +Utility to take leverage the distributed counter functionality. -**Current Release**: 0.1.1 -  **Current Snapshot**: 0.1.2-SNAPSHOT +**Current Release**: 0.2.0 +  **Current Snapshot**: 0.2.1-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=counters/README.md)](counters/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-00BC8E?labelColor=grey&style=flat) [![javadoc](https://javadoc.io/badge2/io.synadia/counters/javadoc.svg)](https://javadoc.io/doc/io.synadia/counters) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters) -### JetStream Scheduled Message +### Scheduled Message Utility to leverage the ability to schedule a message to be published at a later time. Eventually the ability to schedule a message to publish based on a cron or schedule. diff --git a/counters/README.md b/counters/README.md index 745a1f6..749f8c6 100644 --- a/counters/README.md +++ b/counters/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # JetStream Distributed Counters CRDT @@ -6,15 +6,15 @@ Utility to take advantage of the distributed counter functionality. https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md -**Current Release**: 0.1.1 -  **Current Snapshot**: 0.1.2-SNAPSHOT -  **Gradle and Maven** `io.synadia:counter` +**Current Release**: 0.2.0 +  **Current Snapshot**: 0.2.1-SNAPSHOT +  **Gradle and Maven** `io.synadia:counters` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counter-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-00BC8E?labelColor=grey&style=flat) [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter) -[![javadoc](https://javadoc.io/badge2/io.synadia/counter/javadoc.svg)](https://javadoc.io/doc/io.synadia/counter) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters) +[![javadoc](https://javadoc.io/badge2/io.synadia/counters/javadoc.svg)](https://javadoc.io/doc/io.synadia/counters) --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. diff --git a/counters/build.gradle b/counters/build.gradle index 51cfde2..7de1e21 100644 --- a/counters/build.gradle +++ b/counters/build.gradle @@ -13,7 +13,7 @@ plugins { id 'signing' } -def jarVersion = "0.2.0" +def jarVersion = "0.2.1" group = 'io.synadia' def isMerge = System.getenv("BUILD_EVENT") == "push" From 127524de6d907e1d2558500f405e6a8290dda513 Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 8 Oct 2025 10:55:33 -0400 Subject: [PATCH 075/135] Update readme images --- README.md | 2 +- batch-publish/README.md | 2 +- chaos-runner/README.md | 2 +- counters/README.md | 2 +- direct-batch/README.md | 2 +- encoded-kv/README.md | 2 +- js-publish-extensions/README.md | 2 +- request-many/README.md | 2 +- retrier/README.md | 2 +- schedule-message/README.md | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5abe33d..40c8271 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Utility to publish an atomic batch, a group of up to 1000 messages [![javadoc](https://javadoc.io/badge2/io.synadia/batch-publish/javadoc.svg)](https://javadoc.io/doc/io.synadia/batch-publish) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish) -### Distributed Counters CRDT +### Distributed Counters Utility to take leverage the distributed counter functionality. diff --git a/batch-publish/README.md b/batch-publish/README.md index 4c9f403..dc338bd 100644 --- a/batch-publish/README.md +++ b/batch-publish/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # Batch Publish diff --git a/chaos-runner/README.md b/chaos-runner/README.md index b3d6cae..099d146 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # Chaos Runner diff --git a/counters/README.md b/counters/README.md index 749f8c6..f2dc2f3 100644 --- a/counters/README.md +++ b/counters/README.md @@ -1,6 +1,6 @@ Orbit -# JetStream Distributed Counters CRDT +# Distributed Counters Utility to take advantage of the distributed counter functionality. diff --git a/direct-batch/README.md b/direct-batch/README.md index 6e1b484..424c834 100644 --- a/direct-batch/README.md +++ b/direct-batch/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # Direct batch diff --git a/encoded-kv/README.md b/encoded-kv/README.md index 2ecbbf7..dbb59d1 100644 --- a/encoded-kv/README.md +++ b/encoded-kv/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # Encoded Key Value diff --git a/js-publish-extensions/README.md b/js-publish-extensions/README.md index 38c5387..a6763d7 100644 --- a/js-publish-extensions/README.md +++ b/js-publish-extensions/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # JNATS JetStream Publisher Extensions diff --git a/request-many/README.md b/request-many/README.md index ee03146..fd4519b 100644 --- a/request-many/README.md +++ b/request-many/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # Request-Many Utility diff --git a/retrier/README.md b/retrier/README.md index 1054051..3633313 100644 --- a/retrier/README.md +++ b/retrier/README.md @@ -1,4 +1,4 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit # Retrier Utility diff --git a/schedule-message/README.md b/schedule-message/README.md index daff4eb..fd4c8be 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -1,6 +1,6 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) +Orbit -# JetStream Scheduled Message +# Scheduled Message Utility to leverage the ability to schedule a message to be published at a later time. Eventually the ability to schedule a message to publish based on a cron or schedule. From ae0945136b3e5279ecd752553b22509c3d4460c5 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 10 Oct 2025 12:35:44 -0400 Subject: [PATCH 076/135] Utility to generate server error constant file --- utils/.gitignore | 77 ++++ utils/LICENSE | 201 +++++++++ utils/NOTICE | 5 + utils/README.md | 10 + utils/build.gradle | 26 ++ utils/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + utils/gradlew | 183 ++++++++ utils/gradlew.bat | 103 +++++ utils/settings.gradle | 10 + .../api/GenerateServerErrorConstants.java | 120 +++++ .../nats/client/api/ServerErrorConstants.java | 415 ++++++++++++++++++ utils/src/main/resources/placeholder.txt | 1 + utils/src/test/resources/placeholder.txt | 1 + 14 files changed, 1157 insertions(+) create mode 100644 utils/.gitignore create mode 100644 utils/LICENSE create mode 100644 utils/NOTICE create mode 100644 utils/README.md create mode 100644 utils/build.gradle create mode 100644 utils/gradle/wrapper/gradle-wrapper.jar create mode 100644 utils/gradle/wrapper/gradle-wrapper.properties create mode 100644 utils/gradlew create mode 100644 utils/gradlew.bat create mode 100644 utils/settings.gradle create mode 100644 utils/src/main/java/io/nats/client/api/GenerateServerErrorConstants.java create mode 100644 utils/src/main/java/io/nats/client/api/ServerErrorConstants.java create mode 100644 utils/src/main/resources/placeholder.txt create mode 100644 utils/src/test/resources/placeholder.txt diff --git a/utils/.gitignore b/utils/.gitignore new file mode 100644 index 0000000..b3e2ca5 --- /dev/null +++ b/utils/.gitignore @@ -0,0 +1,77 @@ + +# NATS stuff # +############## +gnatsd.log +*.csv + +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +/bin + +# Packages # +############ +*.7z +*.dmg +*.gz +*.iso +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log + +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db + +# Editor Files # +################ +*~ +*.swp +.sts4-cache/* + +# Gradle Files # +################ +.gradle +.m2 + +# Build output directies +/target +*/target +/build +*/build + +# IntelliJ specific files/directories +out +.idea +*.ipr +*.iws +*.iml +atlassian-ide-plugin.xml + +# Eclipse specific files/directories +.classpath +.project +.settings +.metadata + +# NetBeans specific files/directories +.nbattrs + +# VSCode +.vscode/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/target/ diff --git a/utils/LICENSE b/utils/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/utils/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/utils/NOTICE b/utils/NOTICE new file mode 100644 index 0000000..ff3c8b4 --- /dev/null +++ b/utils/NOTICE @@ -0,0 +1,5 @@ +Orbit Java +Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. + +This product includes software developed at +Synadia Communications Inc. \ No newline at end of file diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 0000000..35ac2f9 --- /dev/null +++ b/utils/README.md @@ -0,0 +1,10 @@ +Orbit + +# Utilities + +Miscellaneous Helper Code + + +--- +Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. diff --git a/utils/build.gradle b/utils/build.gradle new file mode 100644 index 0000000..457fedc --- /dev/null +++ b/utils/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java' + id 'java-library' +} + +group = 'io.synadia' +version = "0.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() + maven { + url "https://repo1.maven.org/maven2/" + } + maven { + url "https://central.sonatype.com/repository/maven-snapshots/" + } +} + +dependencies { + implementation 'io.nats:jnats:2.23.0' +} diff --git a/utils/gradle/wrapper/gradle-wrapper.jar b/utils/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 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 literal 0 HcmV?d00001 diff --git a/utils/gradle/wrapper/gradle-wrapper.properties b/utils/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..442d913 --- /dev/null +++ b/utils/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/utils/gradlew b/utils/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/utils/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/utils/gradlew.bat b/utils/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/utils/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/utils/settings.gradle b/utils/settings.gradle new file mode 100644 index 0000000..e9f369e --- /dev/null +++ b/utils/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'utils' diff --git a/utils/src/main/java/io/nats/client/api/GenerateServerErrorConstants.java b/utils/src/main/java/io/nats/client/api/GenerateServerErrorConstants.java new file mode 100644 index 0000000..19b48d7 --- /dev/null +++ b/utils/src/main/java/io/nats/client/api/GenerateServerErrorConstants.java @@ -0,0 +1,120 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.nats.client.api; + +import io.nats.client.support.JsonParser; +import io.nats.client.support.JsonValue; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public final class GenerateServerErrorConstants { + public static final Comparator FILE_ORDER = null; + public static final Comparator CONSTANTS_ALPHA = Comparator.comparing(x -> x.constant); + public static final Comparator ERROR_CODE_ASCENDING = Comparator.comparing(x -> x.errorCode); + + private static final String TARGET_CLASS_NAME = "ServerErrorConstants"; + private static final String TARGET_CLASS_FILE = "C:\\temp\\" + TARGET_CLASS_NAME + ".java"; + private static final String ERRORS_JSON = "https://raw.githubusercontent.com/nats-io/nats-server/refs/heads/main/server/errors.json"; + private static final Comparator COMPARATOR = ERROR_CODE_ASCENDING; + + public static byte[] getJson() throws Exception { + URL url = new URL(ERRORS_JSON); + try (InputStream inputStream = url.openStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + + byte[] buffer = new byte[4096]; + int bytesRead; + + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + return outputStream.toByteArray(); + } + } + + final String constant; + final int code; + final int errorCode; + final String description; + + public GenerateServerErrorConstants(JsonValue item) { + constant = item.map.get("constant").string; + code = item.map.get("code").i; + errorCode = item.map.get("error_code").i; + description = item.map.get("description").string; + } + + public String getConstantLine() { + return String.format(" public static final Error %s = new Error(%d, %d, \"%s\");", + constant, code, errorCode, description); + } + + public String getStaticLine() { + return String.format(" temp.put(%d, %s);", errorCode, constant); + } + + @Override + public String toString() { + return getConstantLine(); + } + + public static void main(String[] args) { + try { + JsonValue jv = JsonParser.parse(getJson()); + + List list = new ArrayList<>(); + for (JsonValue item : jv.array) { + list.add(new GenerateServerErrorConstants(item)); + } + + //noinspection ConstantValue + if (COMPARATOR != null) { + list.sort(COMPARATOR); + } + + try (FileOutputStream out = new FileOutputStream(TARGET_CLASS_FILE)) { + + write(out, "package io.nats.client.api;\n\n" + + "import java.util.Collections;\n" + + "import java.util.HashMap;\n" + + "import java.util.Map;\n\n" + + "public final class "); + write(out, TARGET_CLASS_NAME); + writeln(out, " {\n" + + " public static final Map ERROR_BY_API_ERROR_CODE;\n"); + + for (GenerateServerErrorConstants constant : list) { + System.out.println(constant); + writeln(out, constant.getConstantLine()); + } + writeln(out, "\n static {\n" + + " Map temp = new HashMap<>();"); + for (GenerateServerErrorConstants constant : list) { + writeln(out, constant.getStaticLine()); + } + writeln(out, " ERROR_BY_API_ERROR_CODE = Collections.unmodifiableMap(temp);\n }\n}"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void write(FileOutputStream out, String s) throws IOException { + out.write(s.replace("\\n", System.lineSeparator()).getBytes(StandardCharsets.UTF_8)); + } + + private static void writeln(FileOutputStream out, String s) throws IOException { + write(out, s); + out.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/utils/src/main/java/io/nats/client/api/ServerErrorConstants.java b/utils/src/main/java/io/nats/client/api/ServerErrorConstants.java new file mode 100644 index 0000000..2e4b6ab --- /dev/null +++ b/utils/src/main/java/io/nats/client/api/ServerErrorConstants.java @@ -0,0 +1,415 @@ +package io.nats.client.api; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public final class ServerErrorConstants { + public static final Map ERROR_BY_API_ERROR_CODE; + + public static final Error JSAccountResourcesExceededErr = new Error(400, 10002, "resource limits exceeded for account"); + public static final Error JSBadRequestErr = new Error(400, 10003, "bad request"); + public static final Error JSClusterIncompleteErr = new Error(503, 10004, "incomplete results"); + public static final Error JSClusterNoPeersErrF = new Error(400, 10005, "{err}"); + public static final Error JSClusterNotActiveErr = new Error(500, 10006, "JetStream not in clustered mode"); + public static final Error JSClusterNotAssignedErr = new Error(500, 10007, "JetStream cluster not assigned to this server"); + public static final Error JSClusterNotAvailErr = new Error(503, 10008, "JetStream system temporarily unavailable"); + public static final Error JSClusterNotLeaderErr = new Error(500, 10009, "JetStream cluster can not handle request"); + public static final Error JSClusterRequiredErr = new Error(503, 10010, "JetStream clustering support required"); + public static final Error JSClusterTagsErr = new Error(400, 10011, "tags placement not supported for operation"); + public static final Error JSConsumerCreateErrF = new Error(500, 10012, "{err}"); + public static final Error JSConsumerNameExistErr = new Error(400, 10013, "consumer name already in use"); + public static final Error JSConsumerNotFoundErr = new Error(404, 10014, "consumer not found"); + public static final Error JSSnapshotDeliverSubjectInvalidErr = new Error(400, 10015, "deliver subject not valid"); + public static final Error JSConsumerDurableNameNotInSubjectErr = new Error(400, 10016, "consumer expected to be durable but no durable name set in subject"); + public static final Error JSConsumerDurableNameNotMatchSubjectErr = new Error(400, 10017, "consumer name in subject does not match durable name in request"); + public static final Error JSConsumerDurableNameNotSetErr = new Error(400, 10018, "consumer expected to be durable but a durable name was not set"); + public static final Error JSConsumerEphemeralWithDurableInSubjectErr = new Error(400, 10019, "consumer expected to be ephemeral but detected a durable name set in subject"); + public static final Error JSConsumerEphemeralWithDurableNameErr = new Error(400, 10020, "consumer expected to be ephemeral but a durable name was set in request"); + public static final Error JSStreamExternalApiOverlapErrF = new Error(400, 10021, "stream external api prefix {prefix} must not overlap with {subject}"); + public static final Error JSStreamExternalDelPrefixOverlapsErrF = new Error(400, 10022, "stream external delivery prefix {prefix} overlaps with stream subject {subject}"); + public static final Error JSInsufficientResourcesErr = new Error(503, 10023, "insufficient resources"); + public static final Error JSStreamInvalidExternalDeliverySubjErrF = new Error(400, 10024, "stream external delivery prefix {prefix} must not contain wildcards"); + public static final Error JSInvalidJSONErr = new Error(400, 10025, "invalid JSON: {err}"); + public static final Error JSMaximumConsumersLimitErr = new Error(400, 10026, "maximum consumers limit reached"); + public static final Error JSMaximumStreamsLimitErr = new Error(400, 10027, "maximum number of streams reached"); + public static final Error JSMemoryResourcesExceededErr = new Error(500, 10028, "insufficient memory resources available"); + public static final Error JSMirrorConsumerSetupFailedErrF = new Error(500, 10029, "{err}"); + public static final Error JSMirrorMaxMessageSizeTooBigErr = new Error(400, 10030, "stream mirror must have max message size >= source"); + public static final Error JSMirrorWithSourcesErr = new Error(400, 10031, "stream mirrors can not also contain other sources"); + public static final Error JSMirrorWithStartSeqAndTimeErr = new Error(400, 10032, "stream mirrors can not have both start seq and start time configured"); + public static final Error JSMirrorWithSubjectFiltersErr = new Error(400, 10033, "stream mirrors can not contain filtered subjects"); + public static final Error JSMirrorWithSubjectsErr = new Error(400, 10034, "stream mirrors can not contain subjects"); + public static final Error JSNoAccountErr = new Error(503, 10035, "account not found"); + public static final Error JSClusterUnSupportFeatureErr = new Error(503, 10036, "not currently supported in clustered mode"); + public static final Error JSNoMessageFoundErr = new Error(404, 10037, "no message found"); + public static final Error JSNotEmptyRequestErr = new Error(400, 10038, "expected an empty request payload"); + public static final Error JSNotEnabledForAccountErr = new Error(503, 10039, "JetStream not enabled for account"); + public static final Error JSClusterPeerNotMemberErr = new Error(400, 10040, "peer not a member"); + public static final Error JSRaftGeneralErrF = new Error(500, 10041, "{err}"); + public static final Error JSRestoreSubscribeFailedErrF = new Error(500, 10042, "JetStream unable to subscribe to restore snapshot {subject}: {err}"); + public static final Error JSSequenceNotFoundErrF = new Error(400, 10043, "sequence {seq} not found"); + public static final Error JSClusterServerNotMemberErr = new Error(400, 10044, "server is not a member of the cluster"); + public static final Error JSSourceConsumerSetupFailedErrF = new Error(500, 10045, "{err}"); + public static final Error JSSourceMaxMessageSizeTooBigErr = new Error(400, 10046, "stream source must have max message size >= target"); + public static final Error JSStorageResourcesExceededErr = new Error(500, 10047, "insufficient storage resources available"); + public static final Error JSStreamAssignmentErrF = new Error(500, 10048, "{err}"); + public static final Error JSStreamCreateErrF = new Error(500, 10049, "{err}"); + public static final Error JSStreamDeleteErrF = new Error(500, 10050, "{err}"); + public static final Error JSStreamGeneralErrorF = new Error(500, 10051, "{err}"); + public static final Error JSStreamInvalidConfigF = new Error(500, 10052, "{err}"); + public static final Error JSStreamLimitsErrF = new Error(500, 10053, "{err}"); + public static final Error JSStreamMessageExceedsMaximumErr = new Error(400, 10054, "message size exceeds maximum allowed"); + public static final Error JSStreamMirrorNotUpdatableErr = new Error(400, 10055, "stream mirror configuration can not be updated"); + public static final Error JSStreamMismatchErr = new Error(400, 10056, "stream name in subject does not match request"); + public static final Error JSStreamMsgDeleteFailedF = new Error(500, 10057, "{err}"); + public static final Error JSStreamNameExistErr = new Error(400, 10058, "stream name already in use with a different configuration"); + public static final Error JSStreamNotFoundErr = new Error(404, 10059, "stream not found"); + public static final Error JSStreamNotMatchErr = new Error(400, 10060, "expected stream does not match"); + public static final Error JSStreamReplicasNotUpdatableErr = new Error(400, 10061, "Replicas configuration can not be updated"); + public static final Error JSStreamRestoreErrF = new Error(500, 10062, "restore failed: {err}"); + public static final Error JSStreamSequenceNotMatchErr = new Error(503, 10063, "expected stream sequence does not match"); + public static final Error JSStreamSnapshotErrF = new Error(500, 10064, "snapshot failed: {err}"); + public static final Error JSStreamSubjectOverlapErr = new Error(400, 10065, "subjects overlap with an existing stream"); + public static final Error JSStreamTemplateCreateErrF = new Error(500, 10066, "{err}"); + public static final Error JSStreamTemplateDeleteErrF = new Error(500, 10067, "{err}"); + public static final Error JSStreamTemplateNotFoundErr = new Error(404, 10068, "template not found"); + public static final Error JSStreamUpdateErrF = new Error(500, 10069, "{err}"); + public static final Error JSStreamWrongLastMsgIDErrF = new Error(400, 10070, "wrong last msg ID: {id}"); + public static final Error JSStreamWrongLastSequenceErrF = new Error(400, 10071, "wrong last sequence: {seq}"); + public static final Error JSTempStorageFailedErr = new Error(500, 10072, "JetStream unable to open temp storage for restore"); + public static final Error JSTemplateNameNotMatchSubjectErr = new Error(400, 10073, "template name in subject does not match request"); + public static final Error JSStreamReplicasNotSupportedErr = new Error(500, 10074, "replicas > 1 not supported in non-clustered mode"); + public static final Error JSPeerRemapErr = new Error(503, 10075, "peer remap failed"); + public static final Error JSNotEnabledErr = new Error(503, 10076, "JetStream not enabled"); + public static final Error JSStreamStoreFailedF = new Error(503, 10077, "{err}"); + public static final Error JSConsumerConfigRequiredErr = new Error(400, 10078, "consumer config required"); + public static final Error JSConsumerDeliverToWildcardsErr = new Error(400, 10079, "consumer deliver subject has wildcards"); + public static final Error JSConsumerPushMaxWaitingErr = new Error(400, 10080, "consumer in push mode can not set max waiting"); + public static final Error JSConsumerDeliverCycleErr = new Error(400, 10081, "consumer deliver subject forms a cycle"); + public static final Error JSConsumerMaxPendingAckPolicyRequiredErr = new Error(400, 10082, "consumer requires ack policy for max ack pending"); + public static final Error JSConsumerSmallHeartbeatErr = new Error(400, 10083, "consumer idle heartbeat needs to be >= 100ms"); + public static final Error JSConsumerPullRequiresAckErr = new Error(400, 10084, "consumer in pull mode requires explicit ack policy on workqueue stream"); + public static final Error JSConsumerPullNotDurableErr = new Error(400, 10085, "consumer in pull mode requires a durable name"); + public static final Error JSConsumerPullWithRateLimitErr = new Error(400, 10086, "consumer in pull mode can not have rate limit set"); + public static final Error JSConsumerMaxWaitingNegativeErr = new Error(400, 10087, "consumer max waiting needs to be positive"); + public static final Error JSConsumerHBRequiresPushErr = new Error(400, 10088, "consumer idle heartbeat requires a push based consumer"); + public static final Error JSConsumerFCRequiresPushErr = new Error(400, 10089, "consumer flow control requires a push based consumer"); + public static final Error JSConsumerDirectRequiresPushErr = new Error(400, 10090, "consumer direct requires a push based consumer"); + public static final Error JSConsumerDirectRequiresEphemeralErr = new Error(400, 10091, "consumer direct requires an ephemeral consumer"); + public static final Error JSConsumerOnMappedErr = new Error(400, 10092, "consumer direct on a mapped consumer"); + public static final Error JSConsumerFilterNotSubsetErr = new Error(400, 10093, "consumer filter subject is not a valid subset of the interest subjects"); + public static final Error JSConsumerInvalidPolicyErrF = new Error(400, 10094, "{err}"); + public static final Error JSConsumerInvalidSamplingErrF = new Error(400, 10095, "failed to parse consumer sampling configuration: {err}"); + public static final Error JSStreamInvalidErr = new Error(500, 10096, "stream not valid"); + public static final Error JSStreamHeaderExceedsMaximumErr = new Error(400, 10097, "header size exceeds maximum allowed of 64k"); + public static final Error JSConsumerWQRequiresExplicitAckErr = new Error(400, 10098, "workqueue stream requires explicit ack"); + public static final Error JSConsumerWQMultipleUnfilteredErr = new Error(400, 10099, "multiple non-filtered consumers not allowed on workqueue stream"); + public static final Error JSConsumerWQConsumerNotUniqueErr = new Error(400, 10100, "filtered consumer not unique on workqueue stream"); + public static final Error JSConsumerWQConsumerNotDeliverAllErr = new Error(400, 10101, "consumer must be deliver all on workqueue stream"); + public static final Error JSConsumerNameTooLongErrF = new Error(400, 10102, "consumer name is too long, maximum allowed is {max}"); + public static final Error JSConsumerBadDurableNameErr = new Error(400, 10103, "durable name can not contain '.', '*', '>'"); + public static final Error JSConsumerStoreFailedErrF = new Error(500, 10104, "error creating store for consumer: {err}"); + public static final Error JSConsumerExistingActiveErr = new Error(400, 10105, "consumer already exists and is still active"); + public static final Error JSConsumerReplacementWithDifferentNameErr = new Error(400, 10106, "consumer replacement durable config not the same"); + public static final Error JSConsumerDescriptionTooLongErrF = new Error(400, 10107, "consumer description is too long, maximum allowed is {max}"); + public static final Error JSConsumerWithFlowControlNeedsHeartbeats = new Error(400, 10108, "consumer with flow control also needs heartbeats"); + public static final Error JSStreamSealedErr = new Error(400, 10109, "invalid operation on sealed stream"); + public static final Error JSStreamPurgeFailedF = new Error(500, 10110, "{err}"); + public static final Error JSStreamRollupFailedF = new Error(500, 10111, "{err}"); + public static final Error JSConsumerInvalidDeliverSubject = new Error(400, 10112, "invalid push consumer deliver subject"); + public static final Error JSStreamMaxBytesRequired = new Error(400, 10113, "account requires a stream config to have max bytes set"); + public static final Error JSConsumerMaxRequestBatchNegativeErr = new Error(400, 10114, "consumer max request batch needs to be > 0"); + public static final Error JSConsumerMaxRequestExpiresTooSmall = new Error(400, 10115, "consumer max request expires needs to be >= 1ms"); + public static final Error JSConsumerMaxDeliverBackoffErr = new Error(400, 10116, "max deliver is required to be > length of backoff values"); + public static final Error JSStreamInfoMaxSubjectsErr = new Error(500, 10117, "subject details would exceed maximum allowed"); + public static final Error JSStreamOfflineErr = new Error(500, 10118, "stream is offline"); + public static final Error JSConsumerOfflineErr = new Error(500, 10119, "consumer is offline"); + public static final Error JSNoLimitsErr = new Error(400, 10120, "no JetStream default or applicable tiered limit present"); + public static final Error JSConsumerMaxPendingAckExcessErrF = new Error(400, 10121, "consumer max ack pending exceeds system limit of {limit}"); + public static final Error JSStreamMaxStreamBytesExceeded = new Error(400, 10122, "stream max bytes exceeds account limit max stream bytes"); + public static final Error JSStreamMoveAndScaleErr = new Error(400, 10123, "can not move and scale a stream in a single update"); + public static final Error JSStreamMoveInProgressF = new Error(400, 10124, "stream move already in progress: {msg}"); + public static final Error JSConsumerMaxRequestBatchExceededF = new Error(400, 10125, "consumer max request batch exceeds server limit of {limit}"); + public static final Error JSConsumerReplicasExceedsStream = new Error(400, 10126, "consumer config replica count exceeds parent stream"); + public static final Error JSConsumerNameContainsPathSeparatorsErr = new Error(400, 10127, "Consumer name can not contain path separators"); + public static final Error JSStreamNameContainsPathSeparatorsErr = new Error(400, 10128, "Stream name can not contain path separators"); + public static final Error JSStreamMoveNotInProgress = new Error(400, 10129, "stream move not in progress"); + public static final Error JSStreamNameExistRestoreFailedErr = new Error(400, 10130, "stream name already in use, cannot restore"); + public static final Error JSConsumerCreateFilterSubjectMismatchErr = new Error(400, 10131, "Consumer create request did not match filtered subject from create subject"); + public static final Error JSConsumerCreateDurableAndNameMismatch = new Error(400, 10132, "Consumer Durable and Name have to be equal if both are provided"); + public static final Error JSReplicasCountCannotBeNegative = new Error(400, 10133, "replicas count cannot be negative"); + public static final Error JSConsumerReplicasShouldMatchStream = new Error(400, 10134, "consumer config replicas must match interest retention stream's replicas"); + public static final Error JSConsumerMetadataLengthErrF = new Error(400, 10135, "consumer metadata exceeds maximum size of {limit}"); + public static final Error JSConsumerDuplicateFilterSubjects = new Error(400, 10136, "consumer cannot have both FilterSubject and FilterSubjects specified"); + public static final Error JSConsumerMultipleFiltersNotAllowed = new Error(400, 10137, "consumer with multiple subject filters cannot use subject based API"); + public static final Error JSConsumerOverlappingSubjectFilters = new Error(400, 10138, "consumer subject filters cannot overlap"); + public static final Error JSConsumerEmptyFilter = new Error(400, 10139, "consumer filter in FilterSubjects cannot be empty"); + public static final Error JSSourceDuplicateDetected = new Error(400, 10140, "duplicate source configuration detected"); + public static final Error JSSourceInvalidStreamName = new Error(400, 10141, "sourced stream name is invalid"); + public static final Error JSMirrorInvalidStreamName = new Error(400, 10142, "mirrored stream name is invalid"); + public static final Error JSMirrorWithFirstSeqErr = new Error(400, 10143, "stream mirrors can not have first sequence configured"); + public static final Error JSSourceMultipleFiltersNotAllowed = new Error(400, 10144, "source with multiple subject transforms cannot also have a single subject filter"); + public static final Error JSSourceInvalidSubjectFilter = new Error(400, 10145, "source transform source: {err}"); + public static final Error JSSourceInvalidTransformDestination = new Error(400, 10146, "source transform: {err}"); + public static final Error JSSourceOverlappingSubjectFilters = new Error(400, 10147, "source filters can not overlap"); + public static final Error JSConsumerAlreadyExists = new Error(400, 10148, "consumer already exists"); + public static final Error JSConsumerDoesNotExist = new Error(400, 10149, "consumer does not exist"); + public static final Error JSMirrorMultipleFiltersNotAllowed = new Error(400, 10150, "mirror with multiple subject transforms cannot also have a single subject filter"); + public static final Error JSMirrorInvalidSubjectFilter = new Error(400, 10151, "mirror transform source: {err}"); + public static final Error JSMirrorOverlappingSubjectFilters = new Error(400, 10152, "mirror subject filters can not overlap"); + public static final Error JSConsumerInactiveThresholdExcess = new Error(400, 10153, "consumer inactive threshold exceeds system limit of {limit}"); + public static final Error JSMirrorInvalidTransformDestination = new Error(400, 10154, "mirror transform: {err}"); + public static final Error JSStreamTransformInvalidSource = new Error(400, 10155, "stream transform source: {err}"); + public static final Error JSStreamTransformInvalidDestination = new Error(400, 10156, "stream transform: {err}"); + public static final Error JSPedanticErrF = new Error(400, 10157, "pedantic mode: {err}"); + public static final Error JSStreamDuplicateMessageConflict = new Error(409, 10158, "duplicate message id is in process"); + public static final Error JSConsumerPriorityPolicyWithoutGroup = new Error(400, 10159, "Setting PriorityPolicy requires at least one PriorityGroup to be set"); + public static final Error JSConsumerInvalidPriorityGroupErr = new Error(400, 10160, "Provided priority group does not exist for this consumer"); + public static final Error JSConsumerEmptyGroupName = new Error(400, 10161, "Group name cannot be an empty string"); + public static final Error JSConsumerInvalidGroupNameErr = new Error(400, 10162, "Valid priority group name must match A-Z, a-z, 0-9, -_/=)+ and may not exceed 16 characters"); + public static final Error JSStreamExpectedLastSeqPerSubjectNotReady = new Error(503, 10163, "expected last sequence per subject temporarily unavailable"); + public static final Error JSStreamWrongLastSequenceConstantErr = new Error(400, 10164, "wrong last sequence"); + public static final Error JSMessageTTLInvalidErr = new Error(400, 10165, "invalid per-message TTL"); + public static final Error JSMessageTTLDisabledErr = new Error(400, 10166, "per-message TTL is disabled"); + public static final Error JSStreamTooManyRequests = new Error(429, 10167, "too many requests"); + public static final Error JSMessageIncrDisabledErr = new Error(400, 10168, "message counters is disabled"); + public static final Error JSMessageIncrMissingErr = new Error(400, 10169, "message counter increment is missing"); + public static final Error JSMessageIncrPayloadErr = new Error(400, 10170, "message counter has payload"); + public static final Error JSMessageIncrInvalidErr = new Error(400, 10171, "message counter increment is invalid"); + public static final Error JSMessageCounterBrokenErr = new Error(400, 10172, "message counter is broken"); + public static final Error JSMirrorWithCountersErr = new Error(400, 10173, "stream mirrors can not also calculate counters"); + public static final Error JSAtomicPublishDisabledErr = new Error(400, 10174, "atomic publish is disabled"); + public static final Error JSAtomicPublishMissingSeqErr = new Error(400, 10175, "atomic publish sequence is missing"); + public static final Error JSAtomicPublishIncompleteBatchErr = new Error(400, 10176, "atomic publish batch is incomplete"); + public static final Error JSAtomicPublishUnsupportedHeaderBatchErr = new Error(400, 10177, "atomic publish unsupported header used: {header}"); + public static final Error JSConsumerPushWithPriorityGroupErr = new Error(400, 10178, "priority groups can not be used with push consumers"); + public static final Error JSAtomicPublishInvalidBatchIDErr = new Error(400, 10179, "atomic publish batch ID is invalid"); + public static final Error JSStreamMinLastSeqErr = new Error(412, 10180, "min last sequence"); + public static final Error JSConsumerAckPolicyInvalidErr = new Error(400, 10181, "consumer ack policy invalid"); + public static final Error JSConsumerReplayPolicyInvalidErr = new Error(400, 10182, "consumer replay policy invalid"); + public static final Error JSConsumerAckWaitNegativeErr = new Error(400, 10183, "consumer ack wait needs to be positive"); + public static final Error JSConsumerBackOffNegativeErr = new Error(400, 10184, "consumer backoff needs to be positive"); + public static final Error JSRequiredApiLevelErr = new Error(412, 10185, "JetStream minimum api level required"); + public static final Error JSMirrorWithMsgSchedulesErr = new Error(400, 10186, "stream mirrors can not also schedule messages"); + public static final Error JSSourceWithMsgSchedulesErr = new Error(400, 10187, "stream source can not also schedule messages"); + public static final Error JSMessageSchedulesDisabledErr = new Error(400, 10188, "message schedules is disabled"); + public static final Error JSMessageSchedulesPatternInvalidErr = new Error(400, 10189, "message schedules pattern is invalid"); + public static final Error JSMessageSchedulesTargetInvalidErr = new Error(400, 10190, "message schedules target is invalid"); + public static final Error JSMessageSchedulesTTLInvalidErr = new Error(400, 10191, "message schedules invalid per-message TTL"); + public static final Error JSMessageSchedulesRollupInvalidErr = new Error(400, 10192, "message schedules invalid rollup"); + public static final Error JSStreamExpectedLastSeqPerSubjectInvalid = new Error(400, 10193, "missing sequence for expected last sequence per subject"); + public static final Error JSStreamOfflineReasonErrF = new Error(500, 10194, "stream is offline: {err}"); + public static final Error JSConsumerOfflineReasonErrF = new Error(500, 10195, "consumer is offline: {err}"); + public static final Error JSConsumerPriorityGroupWithPolicyNone = new Error(400, 10196, "consumer can not have priority groups when policy is none"); + public static final Error JSConsumerPinnedTTLWithoutPriorityPolicyNone = new Error(400, 10197, "PinnedTTL cannot be set when PriorityPolicy is none"); + public static final Error JSMirrorWithAtomicPublishErr = new Error(400, 10198, "stream mirrors can not also use atomic publishing"); + public static final Error JSAtomicPublishTooLargeBatchErrF = new Error(400, 10199, "atomic publish batch is too large: {size}"); + public static final Error JSAtomicPublishInvalidBatchCommitErr = new Error(400, 10200, "atomic publish batch commit is invalid"); + public static final Error JSAtomicPublishContainsDuplicateMessageErr = new Error(400, 10201, "atomic publish batch contains duplicate message id"); + + static { + Map temp = new HashMap<>(); + temp.put(10002, JSAccountResourcesExceededErr); + temp.put(10003, JSBadRequestErr); + temp.put(10004, JSClusterIncompleteErr); + temp.put(10005, JSClusterNoPeersErrF); + temp.put(10006, JSClusterNotActiveErr); + temp.put(10007, JSClusterNotAssignedErr); + temp.put(10008, JSClusterNotAvailErr); + temp.put(10009, JSClusterNotLeaderErr); + temp.put(10010, JSClusterRequiredErr); + temp.put(10011, JSClusterTagsErr); + temp.put(10012, JSConsumerCreateErrF); + temp.put(10013, JSConsumerNameExistErr); + temp.put(10014, JSConsumerNotFoundErr); + temp.put(10015, JSSnapshotDeliverSubjectInvalidErr); + temp.put(10016, JSConsumerDurableNameNotInSubjectErr); + temp.put(10017, JSConsumerDurableNameNotMatchSubjectErr); + temp.put(10018, JSConsumerDurableNameNotSetErr); + temp.put(10019, JSConsumerEphemeralWithDurableInSubjectErr); + temp.put(10020, JSConsumerEphemeralWithDurableNameErr); + temp.put(10021, JSStreamExternalApiOverlapErrF); + temp.put(10022, JSStreamExternalDelPrefixOverlapsErrF); + temp.put(10023, JSInsufficientResourcesErr); + temp.put(10024, JSStreamInvalidExternalDeliverySubjErrF); + temp.put(10025, JSInvalidJSONErr); + temp.put(10026, JSMaximumConsumersLimitErr); + temp.put(10027, JSMaximumStreamsLimitErr); + temp.put(10028, JSMemoryResourcesExceededErr); + temp.put(10029, JSMirrorConsumerSetupFailedErrF); + temp.put(10030, JSMirrorMaxMessageSizeTooBigErr); + temp.put(10031, JSMirrorWithSourcesErr); + temp.put(10032, JSMirrorWithStartSeqAndTimeErr); + temp.put(10033, JSMirrorWithSubjectFiltersErr); + temp.put(10034, JSMirrorWithSubjectsErr); + temp.put(10035, JSNoAccountErr); + temp.put(10036, JSClusterUnSupportFeatureErr); + temp.put(10037, JSNoMessageFoundErr); + temp.put(10038, JSNotEmptyRequestErr); + temp.put(10039, JSNotEnabledForAccountErr); + temp.put(10040, JSClusterPeerNotMemberErr); + temp.put(10041, JSRaftGeneralErrF); + temp.put(10042, JSRestoreSubscribeFailedErrF); + temp.put(10043, JSSequenceNotFoundErrF); + temp.put(10044, JSClusterServerNotMemberErr); + temp.put(10045, JSSourceConsumerSetupFailedErrF); + temp.put(10046, JSSourceMaxMessageSizeTooBigErr); + temp.put(10047, JSStorageResourcesExceededErr); + temp.put(10048, JSStreamAssignmentErrF); + temp.put(10049, JSStreamCreateErrF); + temp.put(10050, JSStreamDeleteErrF); + temp.put(10051, JSStreamGeneralErrorF); + temp.put(10052, JSStreamInvalidConfigF); + temp.put(10053, JSStreamLimitsErrF); + temp.put(10054, JSStreamMessageExceedsMaximumErr); + temp.put(10055, JSStreamMirrorNotUpdatableErr); + temp.put(10056, JSStreamMismatchErr); + temp.put(10057, JSStreamMsgDeleteFailedF); + temp.put(10058, JSStreamNameExistErr); + temp.put(10059, JSStreamNotFoundErr); + temp.put(10060, JSStreamNotMatchErr); + temp.put(10061, JSStreamReplicasNotUpdatableErr); + temp.put(10062, JSStreamRestoreErrF); + temp.put(10063, JSStreamSequenceNotMatchErr); + temp.put(10064, JSStreamSnapshotErrF); + temp.put(10065, JSStreamSubjectOverlapErr); + temp.put(10066, JSStreamTemplateCreateErrF); + temp.put(10067, JSStreamTemplateDeleteErrF); + temp.put(10068, JSStreamTemplateNotFoundErr); + temp.put(10069, JSStreamUpdateErrF); + temp.put(10070, JSStreamWrongLastMsgIDErrF); + temp.put(10071, JSStreamWrongLastSequenceErrF); + temp.put(10072, JSTempStorageFailedErr); + temp.put(10073, JSTemplateNameNotMatchSubjectErr); + temp.put(10074, JSStreamReplicasNotSupportedErr); + temp.put(10075, JSPeerRemapErr); + temp.put(10076, JSNotEnabledErr); + temp.put(10077, JSStreamStoreFailedF); + temp.put(10078, JSConsumerConfigRequiredErr); + temp.put(10079, JSConsumerDeliverToWildcardsErr); + temp.put(10080, JSConsumerPushMaxWaitingErr); + temp.put(10081, JSConsumerDeliverCycleErr); + temp.put(10082, JSConsumerMaxPendingAckPolicyRequiredErr); + temp.put(10083, JSConsumerSmallHeartbeatErr); + temp.put(10084, JSConsumerPullRequiresAckErr); + temp.put(10085, JSConsumerPullNotDurableErr); + temp.put(10086, JSConsumerPullWithRateLimitErr); + temp.put(10087, JSConsumerMaxWaitingNegativeErr); + temp.put(10088, JSConsumerHBRequiresPushErr); + temp.put(10089, JSConsumerFCRequiresPushErr); + temp.put(10090, JSConsumerDirectRequiresPushErr); + temp.put(10091, JSConsumerDirectRequiresEphemeralErr); + temp.put(10092, JSConsumerOnMappedErr); + temp.put(10093, JSConsumerFilterNotSubsetErr); + temp.put(10094, JSConsumerInvalidPolicyErrF); + temp.put(10095, JSConsumerInvalidSamplingErrF); + temp.put(10096, JSStreamInvalidErr); + temp.put(10097, JSStreamHeaderExceedsMaximumErr); + temp.put(10098, JSConsumerWQRequiresExplicitAckErr); + temp.put(10099, JSConsumerWQMultipleUnfilteredErr); + temp.put(10100, JSConsumerWQConsumerNotUniqueErr); + temp.put(10101, JSConsumerWQConsumerNotDeliverAllErr); + temp.put(10102, JSConsumerNameTooLongErrF); + temp.put(10103, JSConsumerBadDurableNameErr); + temp.put(10104, JSConsumerStoreFailedErrF); + temp.put(10105, JSConsumerExistingActiveErr); + temp.put(10106, JSConsumerReplacementWithDifferentNameErr); + temp.put(10107, JSConsumerDescriptionTooLongErrF); + temp.put(10108, JSConsumerWithFlowControlNeedsHeartbeats); + temp.put(10109, JSStreamSealedErr); + temp.put(10110, JSStreamPurgeFailedF); + temp.put(10111, JSStreamRollupFailedF); + temp.put(10112, JSConsumerInvalidDeliverSubject); + temp.put(10113, JSStreamMaxBytesRequired); + temp.put(10114, JSConsumerMaxRequestBatchNegativeErr); + temp.put(10115, JSConsumerMaxRequestExpiresTooSmall); + temp.put(10116, JSConsumerMaxDeliverBackoffErr); + temp.put(10117, JSStreamInfoMaxSubjectsErr); + temp.put(10118, JSStreamOfflineErr); + temp.put(10119, JSConsumerOfflineErr); + temp.put(10120, JSNoLimitsErr); + temp.put(10121, JSConsumerMaxPendingAckExcessErrF); + temp.put(10122, JSStreamMaxStreamBytesExceeded); + temp.put(10123, JSStreamMoveAndScaleErr); + temp.put(10124, JSStreamMoveInProgressF); + temp.put(10125, JSConsumerMaxRequestBatchExceededF); + temp.put(10126, JSConsumerReplicasExceedsStream); + temp.put(10127, JSConsumerNameContainsPathSeparatorsErr); + temp.put(10128, JSStreamNameContainsPathSeparatorsErr); + temp.put(10129, JSStreamMoveNotInProgress); + temp.put(10130, JSStreamNameExistRestoreFailedErr); + temp.put(10131, JSConsumerCreateFilterSubjectMismatchErr); + temp.put(10132, JSConsumerCreateDurableAndNameMismatch); + temp.put(10133, JSReplicasCountCannotBeNegative); + temp.put(10134, JSConsumerReplicasShouldMatchStream); + temp.put(10135, JSConsumerMetadataLengthErrF); + temp.put(10136, JSConsumerDuplicateFilterSubjects); + temp.put(10137, JSConsumerMultipleFiltersNotAllowed); + temp.put(10138, JSConsumerOverlappingSubjectFilters); + temp.put(10139, JSConsumerEmptyFilter); + temp.put(10140, JSSourceDuplicateDetected); + temp.put(10141, JSSourceInvalidStreamName); + temp.put(10142, JSMirrorInvalidStreamName); + temp.put(10143, JSMirrorWithFirstSeqErr); + temp.put(10144, JSSourceMultipleFiltersNotAllowed); + temp.put(10145, JSSourceInvalidSubjectFilter); + temp.put(10146, JSSourceInvalidTransformDestination); + temp.put(10147, JSSourceOverlappingSubjectFilters); + temp.put(10148, JSConsumerAlreadyExists); + temp.put(10149, JSConsumerDoesNotExist); + temp.put(10150, JSMirrorMultipleFiltersNotAllowed); + temp.put(10151, JSMirrorInvalidSubjectFilter); + temp.put(10152, JSMirrorOverlappingSubjectFilters); + temp.put(10153, JSConsumerInactiveThresholdExcess); + temp.put(10154, JSMirrorInvalidTransformDestination); + temp.put(10155, JSStreamTransformInvalidSource); + temp.put(10156, JSStreamTransformInvalidDestination); + temp.put(10157, JSPedanticErrF); + temp.put(10158, JSStreamDuplicateMessageConflict); + temp.put(10159, JSConsumerPriorityPolicyWithoutGroup); + temp.put(10160, JSConsumerInvalidPriorityGroupErr); + temp.put(10161, JSConsumerEmptyGroupName); + temp.put(10162, JSConsumerInvalidGroupNameErr); + temp.put(10163, JSStreamExpectedLastSeqPerSubjectNotReady); + temp.put(10164, JSStreamWrongLastSequenceConstantErr); + temp.put(10165, JSMessageTTLInvalidErr); + temp.put(10166, JSMessageTTLDisabledErr); + temp.put(10167, JSStreamTooManyRequests); + temp.put(10168, JSMessageIncrDisabledErr); + temp.put(10169, JSMessageIncrMissingErr); + temp.put(10170, JSMessageIncrPayloadErr); + temp.put(10171, JSMessageIncrInvalidErr); + temp.put(10172, JSMessageCounterBrokenErr); + temp.put(10173, JSMirrorWithCountersErr); + temp.put(10174, JSAtomicPublishDisabledErr); + temp.put(10175, JSAtomicPublishMissingSeqErr); + temp.put(10176, JSAtomicPublishIncompleteBatchErr); + temp.put(10177, JSAtomicPublishUnsupportedHeaderBatchErr); + temp.put(10178, JSConsumerPushWithPriorityGroupErr); + temp.put(10179, JSAtomicPublishInvalidBatchIDErr); + temp.put(10180, JSStreamMinLastSeqErr); + temp.put(10181, JSConsumerAckPolicyInvalidErr); + temp.put(10182, JSConsumerReplayPolicyInvalidErr); + temp.put(10183, JSConsumerAckWaitNegativeErr); + temp.put(10184, JSConsumerBackOffNegativeErr); + temp.put(10185, JSRequiredApiLevelErr); + temp.put(10186, JSMirrorWithMsgSchedulesErr); + temp.put(10187, JSSourceWithMsgSchedulesErr); + temp.put(10188, JSMessageSchedulesDisabledErr); + temp.put(10189, JSMessageSchedulesPatternInvalidErr); + temp.put(10190, JSMessageSchedulesTargetInvalidErr); + temp.put(10191, JSMessageSchedulesTTLInvalidErr); + temp.put(10192, JSMessageSchedulesRollupInvalidErr); + temp.put(10193, JSStreamExpectedLastSeqPerSubjectInvalid); + temp.put(10194, JSStreamOfflineReasonErrF); + temp.put(10195, JSConsumerOfflineReasonErrF); + temp.put(10196, JSConsumerPriorityGroupWithPolicyNone); + temp.put(10197, JSConsumerPinnedTTLWithoutPriorityPolicyNone); + temp.put(10198, JSMirrorWithAtomicPublishErr); + temp.put(10199, JSAtomicPublishTooLargeBatchErrF); + temp.put(10200, JSAtomicPublishInvalidBatchCommitErr); + temp.put(10201, JSAtomicPublishContainsDuplicateMessageErr); + ERROR_BY_API_ERROR_CODE = Collections.unmodifiableMap(temp); + } +} diff --git a/utils/src/main/resources/placeholder.txt b/utils/src/main/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/utils/src/main/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/utils/src/test/resources/placeholder.txt b/utils/src/test/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/utils/src/test/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file From 35e4ab33d2dc40186c2ccbc45db3d3b755511f7b Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 10 Oct 2025 12:43:10 -0400 Subject: [PATCH 077/135] Utility to generate server error constant file --- utils/README.md | 6 ++++++ .../main/java/io/nats/client/api/ServerErrorConstants.java | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/utils/README.md b/utils/README.md index 35ac2f9..936128e 100644 --- a/utils/README.md +++ b/utils/README.md @@ -4,6 +4,12 @@ Miscellaneous Helper Code +### GenerateServerErrorConstants +`GenerateServerErrorConstants` will download the latest errors.json file and generate a java file like `ServerErrorConstants` + +You can use whatever class name you want, +but the generated class be kept in package `io.nats.client.api;` +otherwise it cannot access the packaged scoped `io.nats.client.api.Error` class --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. diff --git a/utils/src/main/java/io/nats/client/api/ServerErrorConstants.java b/utils/src/main/java/io/nats/client/api/ServerErrorConstants.java index 2e4b6ab..8351e4a 100644 --- a/utils/src/main/java/io/nats/client/api/ServerErrorConstants.java +++ b/utils/src/main/java/io/nats/client/api/ServerErrorConstants.java @@ -1,3 +1,8 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +// GENERATED 10/10/2025 + package io.nats.client.api; import java.util.Collections; From d0f5ba3b3e3e9a8dda487e0de01716f801d54b51 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 3 Nov 2025 12:24:44 -0500 Subject: [PATCH 078/135] updated dependency version --- counters/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counters/build.gradle b/counters/build.gradle index 7de1e21..1243908 100644 --- a/counters/build.gradle +++ b/counters/build.gradle @@ -39,7 +39,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.23.0' - implementation 'io.synadia:direct-batch:0.1.3' + implementation 'io.synadia:direct-batch:0.1.4' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'commons-codec:commons-codec:1.18.0' From aa66a749cfb47befa61ed316a6d50428212f4688 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 3 Nov 2025 12:25:09 -0500 Subject: [PATCH 079/135] check in --- build-tester/CODE-OF-CONDUCT.md | 132 +++++++++ build-tester/LICENSE | 201 ++++++++++++++ build-tester/NOTICE | 5 + build-tester/README.md | 3 + build-tester/build.gradle | 235 ++++++++++++++++ build-tester/env.bat | 2 + build-tester/gradle/libs.versions.toml | 12 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + build-tester/gradlew | 251 ++++++++++++++++++ build-tester/gradlew.bat | 94 +++++++ build-tester/key.txt | 45 ++++ build-tester/notes.txt | 32 +++ build-tester/settings.gradle | 8 + .../bt/examples/BuildTesterExample.java | 8 + .../src/examples/resources/example.properties | 1 + .../main/java/io/synadia/bt/BuildTester.java | 10 + .../src/main/javadoc/images/favicon.ico | Bin 0 -> 15406 bytes .../src/main/javadoc/images/nats-logo.png | Bin 0 -> 6533 bytes .../src/main/javadoc/images/synadia-logo.png | Bin 0 -> 19014 bytes build-tester/src/main/javadoc/overview.html | 16 ++ .../java/io/synadia/bt/BuildTesterTest.java | 17 ++ .../src/test/resources/test.properties | 1 + 23 files changed, 1080 insertions(+) create mode 100644 build-tester/CODE-OF-CONDUCT.md create mode 100644 build-tester/LICENSE create mode 100644 build-tester/NOTICE create mode 100644 build-tester/README.md create mode 100644 build-tester/build.gradle create mode 100644 build-tester/env.bat create mode 100644 build-tester/gradle/libs.versions.toml create mode 100644 build-tester/gradle/wrapper/gradle-wrapper.jar create mode 100644 build-tester/gradle/wrapper/gradle-wrapper.properties create mode 100644 build-tester/gradlew create mode 100644 build-tester/gradlew.bat create mode 100644 build-tester/key.txt create mode 100644 build-tester/notes.txt create mode 100644 build-tester/settings.gradle create mode 100644 build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java create mode 100644 build-tester/src/examples/resources/example.properties create mode 100644 build-tester/src/main/java/io/synadia/bt/BuildTester.java create mode 100644 build-tester/src/main/javadoc/images/favicon.ico create mode 100644 build-tester/src/main/javadoc/images/nats-logo.png create mode 100644 build-tester/src/main/javadoc/images/synadia-logo.png create mode 100644 build-tester/src/main/javadoc/overview.html create mode 100644 build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java create mode 100644 build-tester/src/test/resources/test.properties diff --git a/build-tester/CODE-OF-CONDUCT.md b/build-tester/CODE-OF-CONDUCT.md new file mode 100644 index 0000000..7eb3884 --- /dev/null +++ b/build-tester/CODE-OF-CONDUCT.md @@ -0,0 +1,132 @@ +## Community Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/build-tester/LICENSE b/build-tester/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/build-tester/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/build-tester/NOTICE b/build-tester/NOTICE new file mode 100644 index 0000000..0e589c7 --- /dev/null +++ b/build-tester/NOTICE @@ -0,0 +1,5 @@ +Flink Connector For NATS +Copyright (c) 2023-2025 Synadia Communications Inc. All Rights Reserved. + +This product includes software developed at +Synadia Communications Inc. \ No newline at end of file diff --git a/build-tester/README.md b/build-tester/README.md new file mode 100644 index 0000000..ad06d82 --- /dev/null +++ b/build-tester/README.md @@ -0,0 +1,3 @@ +![Synadia](src/main/javadoc/images/synadia-logo.png) + +# Build Tester diff --git a/build-tester/build.gradle b/build-tester/build.gradle new file mode 100644 index 0000000..2acf10c --- /dev/null +++ b/build-tester/build.gradle @@ -0,0 +1,235 @@ +import aQute.bnd.gradle.Bundle +import org.gradle.internal.os.OperatingSystem + +plugins { + id("java") + id("java-library") + id("maven-publish") + id("jacoco") +// id("biz.aQute.bnd.builder") version "7.1.0" + id("biz.aQute.bnd.builder") version "5.1.2" + id("org.gradle.test-retry") version "1.6.4" + id("io.github.gradle-nexus.publish-plugin") version "1.1.0" + id("signing") +} + +def jarVersion = "0.0.4" +group = 'io.synadia' + +def isRelease = System.getenv("BUILD_EVENT") == "release" + +// version is the variable the build actually uses. +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +// java { +// sourceCompatibility = JavaVersion.VERSION_17 +// targetCompatibility = JavaVersion.VERSION_17 +// } + +repositories { + gradlePluginPortal() + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://oss.sonatype.org/content/repositories/releases/" } +} + +dependencies { + implementation 'io.nats:jnats:2.21.5' + implementation 'org.jspecify:jspecify:1.0.0' + implementation 'org.apache.commons:commons-lang3:3.18.0' + + testImplementation 'io.nats:jnats-server-runner:1.2.8' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' +} + +sourceSets { + main { + java { + srcDirs = ['src/main/java','src/examples/java'] + } + resources { + srcDirs = ['src/examples/resources'] + } + } + test { + java { + srcDirs = ['src/test/java'] + } + } +} + +tasks.register('bundle', Bundle) { + from sourceSets.main.output + exclude("io/synadia/examples/**") +} + +jar { + manifest { + attributes( + 'Automatic-Module-Name': 'io.synadia.build.tester', + 'Implementation-Title': 'Build Tester', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io' + ) + } + bnd (['Implementation-Title': 'Java Nats', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'nats.io'] + ) + exclude("io/synadia/examples/**") +} + +test { + // Use junit platform for unit tests + useJUnitPlatform() + testLogging { + exceptionFormat = 'full' + events "started", "passed", "skipped", "failed" + showStandardStreams = true + } + retry { + failOnPassedAfterRetry = false + maxFailures = 3 + maxRetries = 3 + } + systemProperty 'junit.jupiter.execution.timeout.default', '3m' +} + +javadoc { + options.overview = 'src/main/javadoc/overview.html' // relative to source root + source = sourceSets.main.allJava + title = "Synadia Communications Inc. Build Tester" + excludes ['**/examples/**'] + classpath = sourceSets.main.runtimeClasspath + doLast { + if (!OperatingSystem.current().isWindows()) { + exec { + println "Updating favicon on all html files" + workingDir 'build/docs/javadoc' + // Only on linux, mac at this point + commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' + } + copy { + println "Copying images to javadoc folder" + from 'src/main/javadoc/images' + into 'build/docs/javadoc' + } + } + } +} + +tasks.register('examplesJar', Jar) { + archiveClassifier.set('examples') + manifest { + attributes('Implementation-Title': 'Build Tester Examples', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io') + } + from(sourceSets.main.output) { + include "io/synadia/flink/examples/**" + } +} + +tasks.register('javadocJar', Jar) { + archiveClassifier.set('javadoc') + from javadoc +} + +tasks.register('sourcesJar', Jar) { + archiveClassifier.set('sources') + from sourceSets.main.allSource +} + +tasks.register('testsJar', Jar) { + archiveClassifier.set('tests') + from sourceSets.test.allSource +} + +jacoco { + toolVersion = "0.8.12" +} + +jacocoTestReport { + reports { + xml.required = true // coveralls plugin depends on xml format report + html.required = true + } + afterEvaluate { // only report on main library not examples + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, + exclude: ['**/examples**','**/Debug**']) + })) + } +} + +artifacts { + archives javadocJar, sourcesJar, examplesJar, testsJar +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username = System.getenv('OSSRH_USERNAME') + password = System.getenv('OSSRH_PASSWORD') + } + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact examplesJar + artifact javadocJar + artifact testsJar + pom { + name = 'build-tester' + packaging = 'jar' + groupId = group + artifactId = archivesBaseName + description = 'Synadia Communications Inc. Build Tester' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = "synadia" + name = "Synadia" + email = "info@synadia.com" + url = "https://synadia.io" + } + } + } + } + } +} + +if (isRelease) { + signing { + def signingKeyId = System.getenv('SIGNING_KEY_ID') + def signingKey = System.getenv('SIGNING_KEY') + def signingPassword = System.getenv('SIGNING_PASSWORD') + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + sign configurations.archives + sign publishing.publications.mavenJava + } +} + +if (hasProperty('buildScan')) { + buildScan { + termsOfServiceUrl = 'https://gradle.com/help/legal-terms-of-use' + termsOfServiceAgree = 'yes' + } +} \ No newline at end of file diff --git a/build-tester/env.bat b/build-tester/env.bat new file mode 100644 index 0000000..3772712 --- /dev/null +++ b/build-tester/env.bat @@ -0,0 +1,2 @@ +set JAVA_HOME=C:\Program Files\Java\jdk-21 +set PATH=C:\Program Files\Java\jdk-21\bin;%PATH% diff --git a/build-tester/gradle/libs.versions.toml b/build-tester/gradle/libs.versions.toml new file mode 100644 index 0000000..2cfe86a --- /dev/null +++ b/build-tester/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.4.5-jre" +junit-jupiter = "5.12.1" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/build-tester/gradle/wrapper/gradle-wrapper.jar b/build-tester/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..1b33c55baabb587c669f562ae36f953de2481846 GIT binary patch literal 43764 zcma&OWmKeVvL#I6?i3D%6z=Zs?ofE*?rw#G$eqJB ziT4y8-Y@s9rkH0Tz>ll(^xkcTl)CY?rS&9VNd66Yc)g^6)JcWaY(5$5gt z8gr3SBXUTN;~cBgz&})qX%#!Fxom2Yau_`&8)+6aSN7YY+pS410rRUU*>J}qL0TnJ zRxt*7QeUqTh8j)Q&iavh<}L+$Jqz))<`IfKussVk%%Ah-Ti?Eo0hQH!rK%K=#EAw0 zwq@@~XNUXRnv8$;zv<6rCRJ6fPD^hfrh;0K?n z=p!u^3xOgWZ%f3+?+>H)9+w^$Tn1e;?UpVMJb!!;f)`6f&4|8mr+g)^@x>_rvnL0< zvD0Hu_N>$(Li7|Jgu0mRh&MV+<}`~Wi*+avM01E)Jtg=)-vViQKax!GeDc!xv$^mL z{#OVBA$U{(Zr8~Xm|cP@odkHC*1R8z6hcLY#N@3E-A8XEvpt066+3t9L_6Zg6j@9Q zj$$%~yO-OS6PUVrM2s)(T4#6=JpI_@Uz+!6=GdyVU?`!F=d;8#ZB@(5g7$A0(`eqY z8_i@3w$0*es5mrSjhW*qzrl!_LQWs4?VfLmo1Sd@Ztt53+etwzAT^8ow_*7Jp`Y|l z*UgSEwvxq+FYO!O*aLf-PinZYne7Ib6ny3u>MjQz=((r3NTEeU4=-i0LBq3H-VJH< z^>1RE3_JwrclUn9vb7HcGUaFRA0QHcnE;6)hnkp%lY1UII#WPAv?-;c?YH}LWB8Nl z{sx-@Z;QxWh9fX8SxLZk8;kMFlGD3Jc^QZVL4nO)1I$zQwvwM&_!kW+LMf&lApv#< zur|EyC|U@5OQuph$TC_ZU`{!vJp`13e9alaR0Dbn5ikLFH7>eIz4QbV|C=%7)F=qo z_>M&5N)d)7G(A%c>}UCrW!Ql_6_A{?R7&CL`;!KOb3 z8Z=$YkV-IF;c7zs{3-WDEFJzuakFbd*4LWd<_kBE8~BFcv}js_2OowRNzWCtCQ6&k z{&~Me92$m*@e0ANcWKuz)?YjB*VoSTx??-3Cc0l2U!X^;Bv@m87eKHukAljrD54R+ zE;@_w4NPe1>3`i5Qy*3^E9x#VB6?}v=~qIprrrd5|DFkg;v5ixo0IsBmik8=Y;zv2 z%Bcf%NE$a44bk^`i4VwDLTbX=q@j9;JWT9JncQ!+Y%2&HHk@1~*L8-{ZpY?(-a9J-1~<1ltr9i~D9`P{XTIFWA6IG8c4;6bFw*lzU-{+?b&%OcIoCiw00n>A1ra zFPE$y@>ebbZlf(sN_iWBzQKDV zmmaLX#zK!@ZdvCANfwV}9@2O&w)!5gSgQzHdk2Q`jG6KD7S+1R5&F)j6QTD^=hq&7 zHUW+r^da^%V(h(wonR(j?BOiC!;y=%nJvz?*aW&5E87qq;2z`EI(f zBJNNSMFF9U{sR-af5{IY&AtoGcoG)Iq-S^v{7+t0>7N(KRoPj;+2N5;9o_nxIGjJ@ z7bYQK)bX)vEhy~VL%N6g^NE@D5VtV+Q8U2%{ji_=6+i^G%xeskEhH>Sqr194PJ$fB zu1y^){?9Vkg(FY2h)3ZHrw0Z<@;(gd_dtF#6y_;Iwi{yX$?asr?0N0_B*CifEi7<6 zq`?OdQjCYbhVcg+7MSgIM|pJRu~`g?g3x?Tl+V}#$It`iD1j+!x+!;wS0+2e>#g?Z z*EA^k7W{jO1r^K~cD#5pamp+o@8&yw6;%b|uiT?{Wa=4+9<}aXWUuL#ZwN1a;lQod zW{pxWCYGXdEq9qAmvAB904}?97=re$>!I%wxPV#|f#@A*Y=qa%zHlDv^yWbR03%V0 zprLP+b(#fBqxI%FiF*-n8HtH6$8f(P6!H3V^ysgd8de-N(@|K!A< z^qP}jp(RaM9kQ(^K(U8O84?D)aU(g?1S8iWwe)gqpHCaFlJxb*ilr{KTnu4_@5{K- z)n=CCeCrPHO0WHz)dDtkbZfUfVBd?53}K>C5*-wC4hpDN8cGk3lu-ypq+EYpb_2H; z%vP4@&+c2p;thaTs$dc^1CDGlPG@A;yGR5@$UEqk6p58qpw#7lc<+W(WR;(vr(D>W z#(K$vE#uBkT=*q&uaZwzz=P5mjiee6>!lV?c}QIX%ZdkO1dHg>Fa#xcGT6~}1*2m9 zkc7l3ItD6Ie~o_aFjI$Ri=C!8uF4!Ky7iG9QTrxVbsQroi|r)SAon#*B*{}TB-?=@ z8~jJs;_R2iDd!$+n$%X6FO&PYS{YhDAS+U2o4su9x~1+U3z7YN5o0qUK&|g^klZ6X zj_vrM5SUTnz5`*}Hyts9ADwLu#x_L=nv$Z0`HqN`Zo=V>OQI)fh01n~*a%01%cx%0 z4LTFVjmW+ipVQv5rYcn3;d2o4qunWUY!p+?s~X~(ost@WR@r@EuDOSs8*MT4fiP>! zkfo^!PWJJ1MHgKS2D_hc?Bs?isSDO61>ebl$U*9*QY(b=i&rp3@3GV@z>KzcZOxip z^dzA~44;R~cnhWz7s$$v?_8y-k!DZys}Q?4IkSyR!)C0j$(Gm|t#e3|QAOFaV2}36 z?dPNY;@I=FaCwylc_;~kXlZsk$_eLkNb~TIl8QQ`mmH&$*zwwR8zHU*sId)rxHu*K z;yZWa8UmCwju%aSNLwD5fBl^b0Ux1%q8YR*uG`53Mi<`5uA^Dc6Ync)J3N7;zQ*75)hf%a@{$H+%S?SGT)ks60)?6j$ zspl|4Ad6@%-r1t*$tT(en!gIXTUDcsj?28ZEzz)dH)SV3bZ+pjMaW0oc~rOPZP@g! zb9E+ndeVO_Ib9c_>{)`01^`ZS198 z)(t=+{Azi11$eu%aU7jbwuQrO`vLOixuh~%4z@mKr_Oc;F%Uq01fA)^W&y+g16e?rkLhTxV!EqC%2}sx_1u7IBq|}Be&7WI z4I<;1-9tJsI&pQIhj>FPkQV9{(m!wYYV@i5h?A0#BN2wqlEwNDIq06|^2oYVa7<~h zI_OLan0Do*4R5P=a3H9`s5*>xU}_PSztg`+2mv)|3nIy=5#Z$%+@tZnr> zLcTI!Mxa`PY7%{;KW~!=;*t)R_sl<^b>eNO@w#fEt(tPMg_jpJpW$q_DoUlkY|uo> z0-1{ouA#;t%spf*7VjkK&$QrvwUERKt^Sdo)5@?qAP)>}Y!h4(JQ!7{wIdkA+|)bv z&8hBwoX4v|+fie}iTslaBX^i*TjwO}f{V)8*!dMmRPi%XAWc8<_IqK1jUsApk)+~R zNFTCD-h>M5Y{qTQ&0#j@I@tmXGj%rzhTW5%Bkh&sSc=$Fv;M@1y!zvYG5P2(2|(&W zlcbR1{--rJ&s!rB{G-sX5^PaM@3EqWVz_y9cwLR9xMig&9gq(voeI)W&{d6j1jh&< zARXi&APWE1FQWh7eoZjuP z;vdgX>zep^{{2%hem;e*gDJhK1Hj12nBLIJoL<=0+8SVEBx7!4Ea+hBY;A1gBwvY<)tj~T=H`^?3>zeWWm|LAwo*S4Z%bDVUe z6r)CH1H!(>OH#MXFJ2V(U(qxD{4Px2`8qfFLG+=a;B^~Te_Z!r3RO%Oc#ZAHKQxV5 zRYXxZ9T2A%NVJIu5Pu7!Mj>t%YDO$T@M=RR(~mi%sv(YXVl`yMLD;+WZ{vG9(@P#e zMo}ZiK^7^h6TV%cG+;jhJ0s>h&VERs=tuZz^Tlu~%d{ZHtq6hX$V9h)Bw|jVCMudd zwZ5l7In8NT)qEPGF$VSKg&fb0%R2RnUnqa){)V(X(s0U zkCdVZe6wy{+_WhZh3qLp245Y2RR$@g-!9PjJ&4~0cFSHMUn=>dapv)hy}|y91ZWTV zCh=z*!S3_?`$&-eZ6xIXUq8RGl9oK0BJw*TdU6A`LJqX9eS3X@F)g$jLkBWFscPhR zpCv8#KeAc^y>>Y$k^=r|K(DTC}T$0#jQBOwB#@`P6~*IuW_8JxCG}J4va{ zsZzt}tt+cv7=l&CEuVtjD6G2~_Meh%p4RGuY?hSt?(sreO_F}8r7Kp$qQdvCdZnDQ zxzc*qchE*E2=WK)^oRNa>Ttj`fpvF-JZ5tu5>X1xw)J@1!IqWjq)ESBG?J|ez`-Tc zi5a}GZx|w-h%5lNDE_3ho0hEXMoaofo#Z;$8|2;EDF&*L+e$u}K=u?pb;dv$SXeQM zD-~7P0i_`Wk$#YP$=hw3UVU+=^@Kuy$>6?~gIXx636jh{PHly_a2xNYe1l60`|y!7 z(u%;ILuW0DDJ)2%y`Zc~hOALnj1~txJtcdD#o4BCT68+8gZe`=^te6H_egxY#nZH&P*)hgYaoJ^qtmpeea`35Fw)cy!w@c#v6E29co8&D9CTCl%^GV|X;SpneSXzV~LXyRn-@K0Df z{tK-nDWA!q38M1~`xUIt_(MO^R(yNY#9@es9RQbY@Ia*xHhD&=k^T+ zJi@j2I|WcgW=PuAc>hs`(&CvgjL2a9Rx zCbZyUpi8NWUOi@S%t+Su4|r&UoU|ze9SVe7p@f1GBkrjkkq)T}X%Qo1g!SQ{O{P?m z-OfGyyWta+UCXH+-+(D^%kw#A1-U;?9129at7MeCCzC{DNgO zeSqsV>W^NIfTO~4({c}KUiuoH8A*J!Cb0*sp*w-Bg@YfBIPZFH!M}C=S=S7PLLcIG zs7K77g~W)~^|+mx9onzMm0qh(f~OsDTzVmRtz=aZTllgR zGUn~_5hw_k&rll<4G=G+`^Xlnw;jNYDJz@bE?|r866F2hA9v0-8=JO3g}IHB#b`hy zA42a0>{0L7CcabSD+F7?pGbS1KMvT{@1_@k!_+Ki|5~EMGt7T%u=79F)8xEiL5!EJ zzuxQ`NBliCoJMJdwu|);zRCD<5Sf?Y>U$trQ-;xj6!s5&w=9E7)%pZ+1Nh&8nCCwM zv5>Ket%I?cxr3vVva`YeR?dGxbG@pi{H#8@kFEf0Jq6~K4>kt26*bxv=P&jyE#e$| zDJB_~imk^-z|o!2njF2hL*|7sHCnzluhJjwLQGDmC)Y9 zr9ZN`s)uCd^XDvn)VirMgW~qfn1~SaN^7vcX#K1G`==UGaDVVx$0BQnubhX|{e z^i0}>k-;BP#Szk{cFjO{2x~LjK{^Upqd&<+03_iMLp0$!6_$@TbX>8U-f*-w-ew1?`CtD_0y_Lo|PfKi52p?`5$Jzx0E8`M0 zNIb?#!K$mM4X%`Ry_yhG5k@*+n4||2!~*+&pYLh~{`~o(W|o64^NrjP?-1Lgu?iK^ zTX6u3?#$?R?N!{599vg>G8RGHw)Hx&=|g4599y}mXNpM{EPKKXB&+m?==R3GsIq?G zL5fH={=zawB(sMlDBJ+{dgb)Vx3pu>L=mDV0{r1Qs{0Pn%TpopH{m(By4;{FBvi{I z$}x!Iw~MJOL~&)p93SDIfP3x%ROjg}X{Sme#hiJ&Yk&a;iR}V|n%PriZBY8SX2*;6 z4hdb^&h;Xz%)BDACY5AUsV!($lib4>11UmcgXKWpzRL8r2Srl*9Y(1uBQsY&hO&uv znDNff0tpHlLISam?o(lOp#CmFdH<6HmA0{UwfU#Y{8M+7od8b8|B|7ZYR9f<#+V|ZSaCQvI$~es~g(Pv{2&m_rKSB2QQ zMvT}$?Ll>V+!9Xh5^iy3?UG;dF-zh~RL#++roOCsW^cZ&({6q|?Jt6`?S8=16Y{oH zp50I7r1AC1(#{b`Aq5cw>ypNggHKM9vBx!W$eYIzD!4KbLsZGr2o8>g<@inmS3*>J zx8oG((8f!ei|M@JZB`p7+n<Q}?>h249<`7xJ?u}_n;Gq(&km#1ULN87CeTO~FY zS_Ty}0TgQhV zOh3T7{{x&LSYGQfKR1PDIkP!WnfC1$l+fs@Di+d4O=eVKeF~2fq#1<8hEvpwuqcaH z4A8u~r^gnY3u6}zj*RHjk{AHhrrDqaj?|6GaVJbV%o-nATw}ASFr!f`Oz|u_QPkR# z0mDudY1dZRlk@TyQ?%Eti=$_WNFtLpSx9=S^be{wXINp%MU?a`F66LNU<c;0&ngifmP9i;bj6&hdGMW^Kf8e6ZDXbQD&$QAAMo;OQ)G zW(qlHh;}!ZP)JKEjm$VZjTs@hk&4{?@+NADuYrr!R^cJzU{kGc1yB?;7mIyAWwhbeA_l_lw-iDVi7wcFurf5 z#Uw)A@a9fOf{D}AWE%<`s1L_AwpZ?F!Vac$LYkp<#A!!`XKaDC{A%)~K#5z6>Hv@V zBEqF(D5?@6r3Pwj$^krpPDCjB+UOszqUS;b2n>&iAFcw<*im2(b3|5u6SK!n9Sg4I z0KLcwA6{Mq?p%t>aW0W!PQ>iUeYvNjdKYqII!CE7SsS&Rj)eIw-K4jtI?II+0IdGq z2WT|L3RL?;GtGgt1LWfI4Ka`9dbZXc$TMJ~8#Juv@K^1RJN@yzdLS8$AJ(>g!U9`# zx}qr7JWlU+&m)VG*Se;rGisutS%!6yybi%B`bv|9rjS(xOUIvbNz5qtvC$_JYY+c& za*3*2$RUH8p%pSq>48xR)4qsp!Q7BEiJ*`^>^6INRbC@>+2q9?x(h0bpc>GaNFi$K zPH$6!#(~{8@0QZk=)QnM#I=bDx5vTvjm$f4K}%*s+((H2>tUTf==$wqyoI`oxI7>C z&>5fe)Yg)SmT)eA(|j@JYR1M%KixxC-Eceknf-;N=jJTwKvk#@|J^&5H0c+%KxHUI z6dQbwwVx3p?X<_VRVb2fStH?HH zFR@Mp=qX%#L3XL)+$PXKV|o|#DpHAoqvj6uQKe@M-mnhCSou7Dj4YuO6^*V`m)1lf z;)@e%1!Qg$10w8uEmz{ENb$^%u}B;J7sDd zump}onoD#!l=agcBR)iG!3AF0-63%@`K9G(CzKrm$VJ{v7^O9Ps7Zej|3m= zVXlR&yW6=Y%mD30G@|tf=yC7-#L!16Q=dq&@beWgaIL40k0n% z)QHrp2Jck#evLMM1RGt3WvQ936ZC9vEje0nFMfvmOHVI+&okB_K|l-;|4vW;qk>n~ z+|kk8#`K?x`q>`(f6A${wfw9Cx(^)~tX7<#TpxR#zYG2P+FY~mG{tnEkv~d6oUQA+ z&hNTL=~Y@rF`v-RZlts$nb$3(OL1&@Y11hhL9+zUb6)SP!;CD)^GUtUpCHBE`j1te zAGud@miCVFLk$fjsrcpjsadP__yj9iEZUW{Ll7PPi<$R;m1o!&Xdl~R_v0;oDX2z^!&8}zNGA}iYG|k zmehMd1%?R)u6R#<)B)1oe9TgYH5-CqUT8N7K-A-dm3hbm_W21p%8)H{O)xUlBVb+iUR}-v5dFaCyfSd zC6Bd7=N4A@+Bna=!-l|*_(nWGDpoyU>nH=}IOrLfS+-d40&(Wo*dDB9nQiA2Tse$R z;uq{`X7LLzP)%Y9aHa4YQ%H?htkWd3Owv&UYbr5NUDAH^<l@Z0Cx%`N+B*i!!1u>D8%;Qt1$ zE5O0{-`9gdDxZ!`0m}ywH!;c{oBfL-(BH<&SQ~smbcobU!j49O^f4&IIYh~f+hK*M zZwTp%{ZSAhMFj1qFaOA+3)p^gnXH^=)`NTYgTu!CLpEV2NF=~-`(}7p^Eof=@VUbd z_9U|8qF7Rueg&$qpSSkN%%%DpbV?8E8ivu@ensI0toJ7Eas^jyFReQ1JeY9plb^{m z&eQO)qPLZQ6O;FTr*aJq=$cMN)QlQO@G&%z?BKUs1&I^`lq>=QLODwa`(mFGC`0H< zOlc*|N?B5&!U6BuJvkL?s1&nsi$*5cCv7^j_*l&$-sBmRS85UIrE--7eD8Gr3^+o? zqG-Yl4S&E;>H>k^a0GdUI(|n1`ws@)1%sq2XBdK`mqrNq_b4N{#VpouCXLzNvjoFv zo9wMQ6l0+FT+?%N(ka*;%m~(?338bu32v26!{r)|w8J`EL|t$}TA4q_FJRX5 zCPa{hc_I(7TGE#@rO-(!$1H3N-C0{R$J=yPCXCtGk{4>=*B56JdXU9cQVwB`6~cQZ zf^qK21x_d>X%dT!!)CJQ3mlHA@ z{Prkgfs6=Tz%63$6Zr8CO0Ak3A)Cv#@BVKr&aiKG7RYxY$Yx>Bj#3gJk*~Ps-jc1l z;4nltQwwT4@Z)}Pb!3xM?+EW0qEKA)sqzw~!C6wd^{03-9aGf3Jmt=}w-*!yXupLf z;)>-7uvWN4Unn8b4kfIza-X=x*e4n5pU`HtgpFFd))s$C@#d>aUl3helLom+RYb&g zI7A9GXLRZPl}iQS*d$Azxg-VgcUr*lpLnbPKUV{QI|bsG{8bLG<%CF( zMoS4pRDtLVYOWG^@ox^h8xL~afW_9DcE#^1eEC1SVSb1BfDi^@g?#f6e%v~Aw>@w- zIY0k+2lGWNV|aA*e#`U3=+oBDmGeInfcL)>*!w|*;mWiKNG6wP6AW4-4imN!W)!hE zA02~S1*@Q`fD*+qX@f3!2yJX&6FsEfPditB%TWo3=HA;T3o2IrjS@9SSxv%{{7&4_ zdS#r4OU41~GYMiib#z#O;zohNbhJknrPPZS6sN$%HB=jUnlCO_w5Gw5EeE@KV>soy z2EZ?Y|4RQDDjt5y!WBlZ(8M)|HP<0YyG|D%RqD+K#e7-##o3IZxS^wQ5{Kbzb6h(i z#(wZ|^ei>8`%ta*!2tJzwMv+IFHLF`zTU8E^Mu!R*45_=ccqI};Zbyxw@U%a#2}%f zF>q?SrUa_a4H9l+uW8JHh2Oob>NyUwG=QH~-^ZebU*R@67DcXdz2{HVB4#@edz?B< z5!rQH3O0>A&ylROO%G^fimV*LX7>!%re{_Sm6N>S{+GW1LCnGImHRoF@csnFzn@P0 zM=jld0z%oz;j=>c7mMwzq$B^2mae7NiG}%>(wtmsDXkWk{?BeMpTrIt3Mizq?vRsf zi_WjNp+61uV(%gEU-Vf0;>~vcDhe(dzWdaf#4mH3o^v{0EWhj?E?$5v02sV@xL0l4 zX0_IMFtQ44PfWBbPYN#}qxa%=J%dlR{O!KyZvk^g5s?sTNycWYPJ^FK(nl3k?z-5t z39#hKrdO7V(@!TU)LAPY&ngnZ1MzLEeEiZznn7e-jLCy8LO zu^7_#z*%I-BjS#Pg-;zKWWqX-+Ly$T!4`vTe5ZOV0j?TJVA*2?*=82^GVlZIuH%9s zXiV&(T(QGHHah=s&7e|6y?g+XxZGmK55`wGV>@1U)Th&=JTgJq>4mI&Av2C z)w+kRoj_dA!;SfTfkgMPO>7Dw6&1*Hi1q?54Yng`JO&q->^CX21^PrU^JU#CJ_qhV zSG>afB%>2fx<~g8p=P8Yzxqc}s@>>{g7}F!;lCXvF#RV)^fyYb_)iKVCz1xEq=fJ| z0a7DMCK*FuP=NM*5h;*D`R4y$6cpW-E&-i{v`x=Jbk_xSn@2T3q!3HoAOB`@5Vg6) z{PW|@9o!e;v1jZ2{=Uw6S6o{g82x6g=k!)cFSC*oemHaVjg?VpEmtUuD2_J^A~$4* z3O7HsbA6wxw{TP5Kk)(Vm?gKo+_}11vbo{Tp_5x79P~#F)ahQXT)tSH5;;14?s)On zel1J>1x>+7;g1Iz2FRpnYz;sD0wG9Q!vuzE9yKi3@4a9Nh1!GGN?hA)!mZEnnHh&i zf?#ZEN2sFbf~kV;>K3UNj1&vFhc^sxgj8FCL4v>EOYL?2uuT`0eDH}R zmtUJMxVrV5H{L53hu3#qaWLUa#5zY?f5ozIn|PkMWNP%n zWB5!B0LZB0kLw$k39=!akkE9Q>F4j+q434jB4VmslQ;$ zKiO#FZ`p|dKS716jpcvR{QJkSNfDVhr2%~eHrW;fU45>>snr*S8Vik-5eN5k*c2Mp zyxvX&_cFbB6lODXznHHT|rsURe2!swomtrqc~w5 zymTM8!w`1{04CBprR!_F{5LB+2_SOuZN{b*!J~1ZiPpP-M;);!ce!rOPDLtgR@Ie1 zPreuqm4!H)hYePcW1WZ0Fyaqe%l}F~Orr)~+;mkS&pOhP5Ebb`cnUt!X_QhP4_4p( z8YKQCDKGIy>?WIFm3-}Br2-N`T&FOi?t)$hjphB9wOhBXU#Hb+zm&We_-O)s(wc`2 z8?VsvU;J>Ju7n}uUb3s1yPx_F*|FlAi=Ge=-kN?1;`~6szP%$3B0|8Sqp%ebM)F8v zADFrbeT0cgE>M0DMV@_Ze*GHM>q}wWMzt|GYC%}r{OXRG3Ij&<+nx9;4jE${Fj_r* z`{z1AW_6Myd)i6e0E-h&m{{CvzH=Xg!&(bLYgRMO_YVd8JU7W+7MuGWNE=4@OvP9+ zxi^vqS@5%+#gf*Z@RVyU9N1sO-(rY$24LGsg1>w>s6ST^@)|D9>cT50maXLUD{Fzf zt~tp{OSTEKg3ZSQyQQ5r51){%=?xlZ54*t1;Ow)zLe3i?8tD8YyY^k%M)e`V*r+vL zPqUf&m)U+zxps+NprxMHF{QSxv}>lE{JZETNk1&F+R~bp{_T$dbXL2UGnB|hgh*p4h$clt#6;NO~>zuyY@C-MD@)JCc5XrYOt`wW7! z_ti2hhZBMJNbn0O-uTxl_b6Hm313^fG@e;RrhIUK9@# z+DHGv_Ow$%S8D%RB}`doJjJy*aOa5mGHVHz0e0>>O_%+^56?IkA5eN+L1BVCp4~m=1eeL zb;#G!#^5G%6Mw}r1KnaKsLvJB%HZL)!3OxT{k$Yo-XrJ?|7{s4!H+S2o?N|^Z z)+?IE9H7h~Vxn5hTis^3wHYuOU84+bWd)cUKuHapq=&}WV#OxHpLab`NpwHm8LmOo zjri+!k;7j_?FP##CpM+pOVx*0wExEex z@`#)K<-ZrGyArK;a%Km`^+We|eT+#MygHOT6lXBmz`8|lyZOwL1+b+?Z$0OhMEp3R z&J=iRERpv~TC=p2-BYLC*?4 zxvPs9V@g=JT0>zky5Poj=fW_M!c)Xxz1<=&_ZcL=LMZJqlnO1P^xwGGW*Z+yTBvbV z-IFe6;(k1@$1;tS>{%pXZ_7w+i?N4A2=TXnGf=YhePg8bH8M|Lk-->+w8Y+FjZ;L=wSGwxfA`gqSn)f(XNuSm>6Y z@|#e-)I(PQ^G@N`%|_DZSb4_pkaEF0!-nqY+t#pyA>{9^*I-zw4SYA1_z2Bs$XGUZbGA;VeMo%CezHK0lO={L%G)dI-+8w?r9iexdoB{?l zbJ}C?huIhWXBVs7oo{!$lOTlvCLZ_KN1N+XJGuG$rh<^eUQIqcI7^pmqhBSaOKNRq zrx~w^?9C?*&rNwP_SPYmo;J-#!G|{`$JZK7DxsM3N^8iR4vvn>E4MU&Oe1DKJvLc~ zCT>KLZ1;t@My zRj_2hI^61T&LIz)S!+AQIV23n1>ng+LUvzv;xu!4;wpqb#EZz;F)BLUzT;8UA1x*6vJ zicB!3Mj03s*kGV{g`fpC?V^s(=JG-k1EMHbkdP4P*1^8p_TqO|;!Zr%GuP$8KLxuf z=pv*H;kzd;P|2`JmBt~h6|GxdU~@weK5O=X&5~w$HpfO}@l-T7@vTCxVOwCkoPQv8 z@aV_)I5HQtfs7^X=C03zYmH4m0S!V@JINm6#(JmZRHBD?T!m^DdiZJrhKpBcur2u1 zf9e4%k$$vcFopK5!CC`;ww(CKL~}mlxK_Pv!cOsFgVkNIghA2Au@)t6;Y3*2gK=5d z?|@1a)-(sQ%uFOmJ7v2iG&l&m^u&^6DJM#XzCrF%r>{2XKyxLD2rgWBD;i(!e4InDQBDg==^z;AzT2z~OmV0!?Z z0S9pX$+E;w3WN;v&NYT=+G8hf=6w0E1$0AOr61}eOvE8W1jX%>&Mjo7&!ulawgzLH zbcb+IF(s^3aj12WSi#pzIpijJJzkP?JzRawnxmNDSUR#7!29vHULCE<3Aa#be}ie~d|!V+ z%l~s9Odo$G&fH!t!+`rUT0T9DulF!Yq&BfQWFZV1L9D($r4H(}Gnf6k3^wa7g5|Ws zj7%d`!3(0bb55yhC6@Q{?H|2os{_F%o=;-h{@Yyyn*V7?{s%Grvpe!H^kl6tF4Zf5 z{Jv1~yZ*iIWL_9C*8pBMQArfJJ0d9Df6Kl#wa}7Xa#Ef_5B7=X}DzbQXVPfCwTO@9+@;A^Ti6il_C>g?A-GFwA0#U;t4;wOm-4oS})h z5&on>NAu67O?YCQr%7XIzY%LS4bha9*e*4bU4{lGCUmO2UQ2U)QOqClLo61Kx~3dI zmV3*(P6F_Tr-oP%x!0kTnnT?Ep5j;_IQ^pTRp=e8dmJtI4YgWd0}+b2=ATkOhgpXe z;jmw+FBLE}UIs4!&HflFr4)vMFOJ19W4f2^W(=2)F%TAL)+=F>IE$=e=@j-*bFLSg z)wf|uFQu+!=N-UzSef62u0-C8Zc7 zo6@F)c+nZA{H|+~7i$DCU0pL{0Ye|fKLuV^w!0Y^tT$isu%i1Iw&N|tX3kwFKJN(M zXS`k9js66o$r)x?TWL}Kxl`wUDUpwFx(w4Yk%49;$sgVvT~n8AgfG~HUcDt1TRo^s zdla@6heJB@JV z!vK;BUMznhzGK6PVtj0)GB=zTv6)Q9Yt@l#fv7>wKovLobMV-+(8)NJmyF8R zcB|_K7=FJGGn^X@JdFaat0uhKjp3>k#^&xE_}6NYNG?kgTp>2Iu?ElUjt4~E-?`Du z?mDCS9wbuS%fU?5BU@Ijx>1HG*N?gIP+<~xE4u=>H`8o((cS5M6@_OK%jSjFHirQK zN9@~NXFx*jS{<|bgSpC|SAnA@I)+GB=2W|JJChLI_mx+-J(mSJ!b)uUom6nH0#2^(L@JBlV#t zLl?j54s`Y3vE^c_3^Hl0TGu*tw_n?@HyO@ZrENxA+^!)OvUX28gDSF*xFtQzM$A+O zCG=n#6~r|3zt=8%GuG} z<#VCZ%2?3Q(Ad#Y7GMJ~{U3>E{5e@z6+rgZLX{Cxk^p-7dip^d29;2N1_mm4QkASo z-L`GWWPCq$uCo;X_BmGIpJFBlhl<8~EG{vOD1o|X$aB9KPhWO_cKiU*$HWEgtf=fn zsO%9bp~D2c@?*K9jVN@_vhR03>M_8h!_~%aN!Cnr?s-!;U3SVfmhRwk11A^8Ns`@KeE}+ zN$H}a1U6E;*j5&~Og!xHdfK5M<~xka)x-0N)K_&e7AjMz`toDzasH+^1bZlC!n()crk9kg@$(Y{wdKvbuUd04N^8}t1iOgsKF zGa%%XWx@WoVaNC1!|&{5ZbkopFre-Lu(LCE5HWZBoE#W@er9W<>R=^oYxBvypN#x3 zq#LC8&q)GFP=5^-bpHj?LW=)-g+3_)Ylps!3^YQ{9~O9&K)xgy zMkCWaApU-MI~e^cV{Je75Qr7eF%&_H)BvfyKL=gIA>;OSq(y z052BFz3E(Prg~09>|_Z@!qj}@;8yxnw+#Ej0?Rk<y}4ghbD569B{9hSFr*^ygZ zr6j7P#gtZh6tMk6?4V$*Jgz+#&ug;yOr>=qdI#9U&^am2qoh4Jy}H2%a|#Fs{E(5r z%!ijh;VuGA6)W)cJZx+;9Bp1LMUzN~x_8lQ#D3+sL{be-Jyeo@@dv7XguJ&S5vrH` z>QxOMWn7N-T!D@1(@4>ZlL^y5>m#0!HKovs12GRav4z!>p(1~xok8+_{| z#Ae4{9#NLh#Vj2&JuIn5$d6t@__`o}umFo(n0QxUtd2GKCyE+erwXY?`cm*h&^9*8 zJ+8x6fRZI-e$CRygofIQN^dWysCxgkyr{(_oBwwSRxZora1(%(aC!5BTtj^+YuevI zx?)H#(xlALUp6QJ!=l9N__$cxBZ5p&7;qD3PsXRFVd<({Kh+mShFWJNpy`N@ab7?9 zv5=klvCJ4bx|-pvOO2-+G)6O?$&)ncA#Urze2rlBfp#htudhx-NeRnJ@u%^_bfw4o z4|{b8SkPV3b>Wera1W(+N@p9H>dc6{cnkh-sgr?e%(YkWvK+0YXVwk0=d`)}*47*B z5JGkEdVix!w7-<%r0JF~`ZMMPe;f0EQHuYHxya`puazyph*ZSb1mJAt^k4549BfS; zK7~T&lRb=W{s&t`DJ$B}s-eH1&&-wEOH1KWsKn0a(ZI+G!v&W4A*cl>qAvUv6pbUR z#(f#EKV8~hk&8oayBz4vaswc(?qw1vn`yC zZQDl2PCB-&Uu@g9ZQHhO+v(W0bNig{-k0;;`+wM@#@J)8r?qOYs#&vUna8ILxN7S{ zp1s41KnR8miQJtJtOr|+qk}wrLt+N*z#5o`TmD1)E&QD(Vh&pjZJ_J*0!8dy_ z>^=@v=J)C`x&gjqAYu`}t^S=DFCtc0MkBU2zf|69?xW`Ck~(6zLD)gSE{7n~6w8j_ zoH&~$ED2k5-yRa0!r8fMRy z;QjBYUaUnpd}mf%iVFPR%Dg9!d>g`01m~>2s))`W|5!kc+_&Y>wD@@C9%>-lE`WB0 zOIf%FVD^cj#2hCkFgi-fgzIfOi+ya)MZK@IZhHT5FVEaSbv-oDDs0W)pA0&^nM0TW zmgJmd7b1R7b0a`UwWJYZXp4AJPteYLH>@M|xZFKwm!t3D3&q~av?i)WvAKHE{RqpD{{%OhYkK?47}+}` zrR2(Iv9bhVa;cDzJ%6ntcSbx7v7J@Y4x&+eWSKZ*eR7_=CVIUSB$^lfYe@g+p|LD{ zPSpQmxx@b$%d!05|H}WzBT4_cq?@~dvy<7s&QWtieJ9)hd4)$SZz}#H2UTi$CkFWW|I)v_-NjuH!VypONC=1`A=rm_jfzQ8Fu~1r8i{q-+S_j$ z#u^t&Xnfi5tZtl@^!fUJhx@~Cg0*vXMK}D{>|$#T*+mj(J_@c{jXBF|rm4-8%Z2o! z2z0o(4%8KljCm^>6HDK!{jI7p+RAPcty_~GZ~R_+=+UzZ0qzOwD=;YeZt*?3%UGdr z`c|BPE;yUbnyARUl&XWSNJ<+uRt%!xPF&K;(l$^JcA_CMH6)FZt{>6ah$|(9$2fc~ z=CD00uHM{qv;{Zk9FR0~u|3|Eiqv9?z2#^GqylT5>6JNZwKqKBzzQpKU2_pmtD;CT zi%Ktau!Y2Tldfu&b0UgmF(SSBID)15*r08eoUe#bT_K-G4VecJL2Pa=6D1K6({zj6 za(2Z{r!FY5W^y{qZ}08+h9f>EKd&PN90f}Sc0ejf%kB4+f#T8Q1=Pj=~#pi$U zp#5rMR%W25>k?<$;$x72pkLibu1N|jX4cWjD3q^Pk3js!uK6h7!dlvw24crL|MZs_ zb%Y%?Fyp0bY0HkG^XyS76Ts*|Giw{31LR~+WU5NejqfPr73Rp!xQ1mLgq@mdWncLy z%8}|nzS4P&`^;zAR-&nm5f;D-%yNQPwq4N7&yULM8bkttkD)hVU>h>t47`{8?n2&4 zjEfL}UEagLUYwdx0sB2QXGeRmL?sZ%J!XM`$@ODc2!y|2#7hys=b$LrGbvvjx`Iqi z&RDDm3YBrlKhl`O@%%&rhLWZ*ABFz2nHu7k~3@e4)kO3%$=?GEFUcCF=6-1n!x^vmu+Ai*amgXH+Rknl6U>#9w;A} zn2xanZSDu`4%%x}+~FG{Wbi1jo@wqBc5(5Xl~d0KW(^Iu(U3>WB@-(&vn_PJt9{1`e9Iic@+{VPc`vP776L*viP{wYB2Iff8hB%E3|o zGMOu)tJX!`qJ}ZPzq7>=`*9TmETN7xwU;^AmFZ-ckZjV5B2T09pYliaqGFY|X#E-8 z20b>y?(r-Fn5*WZ-GsK}4WM>@TTqsxvSYWL6>18q8Q`~JO1{vLND2wg@58OaU!EvT z1|o+f1mVXz2EKAbL!Q=QWQKDZpV|jznuJ}@-)1&cdo z^&~b4Mx{*1gurlH;Vhk5g_cM&6LOHS2 zRkLfO#HabR1JD4Vc2t828dCUG#DL}f5QDSBg?o)IYYi@_xVwR2w_ntlpAW0NWk$F1 z$If?*lP&Ka1oWfl!)1c3fl`g*lMW3JOn#)R1+tfwrs`aiFUgz3;XIJ>{QFxLCkK30 zNS-)#DON3yb!7LBHQJ$)4y%TN82DC2-9tOIqzhZ27@WY^<6}vXCWcR5iN{LN8{0u9 zNXayqD=G|e?O^*ms*4P?G%o@J1tN9_76e}E#66mr89%W_&w4n66~R;X_vWD(oArwj z4CpY`)_mH2FvDuxgT+akffhX0b_slJJ*?Jn3O3~moqu2Fs1oL*>7m=oVek2bnprnW zixkaIFU%+3XhNA@@9hyhFwqsH2bM|`P?G>i<-gy>NflhrN{$9?LZ1ynSE_Mj0rADF zhOz4FnK}wpLmQuV zgO4_Oz9GBu_NN>cPLA=`SP^$gxAnj;WjJnBi%Q1zg`*^cG;Q)#3Gv@c^j6L{arv>- zAW%8WrSAVY1sj$=umcAf#ZgC8UGZGoamK}hR7j6}i8#np8ruUlvgQ$j+AQglFsQQq zOjyHf22pxh9+h#n$21&$h?2uq0>C9P?P=Juw0|;oE~c$H{#RGfa>| zj)Iv&uOnaf@foiBJ}_;zyPHcZt1U~nOcNB{)og8Btv+;f@PIT*xz$x!G?u0Di$lo7 zOugtQ$Wx|C($fyJTZE1JvR~i7LP{ zbdIwqYghQAJi9p}V&$=*2Azev$6K@pyblphgpv8^9bN!?V}{BkC!o#bl&AP!3DAjM zmWFsvn2fKWCfjcAQmE+=c3Y7j@#7|{;;0f~PIodmq*;W9Fiak|gil6$w3%b_Pr6K_ zJEG@&!J%DgBZJDCMn^7mk`JV0&l07Bt`1ymM|;a)MOWz*bh2#d{i?SDe9IcHs7 zjCrnyQ*Y5GzIt}>`bD91o#~5H?4_nckAgotN{2%!?wsSl|LVmJht$uhGa+HiH>;av z8c?mcMYM7;mvWr6noUR{)gE!=i7cZUY7e;HXa221KkRoc2UB>s$Y(k%NzTSEr>W(u z<(4mcc)4rB_&bPzX*1?*ra%VF}P1nwiP5cykJ&W{!OTlz&Td0pOkVp+wc z@k=-Hg=()hNg=Q!Ub%`BONH{ z_=ZFgetj@)NvppAK2>8r!KAgi>#%*7;O-o9MOOfQjV-n@BX6;Xw;I`%HBkk20v`qoVd0)}L6_49y1IhR z_OS}+eto}OPVRn*?UHC{eGyFU7JkPz!+gX4P>?h3QOwGS63fv4D1*no^6PveUeE5% zlehjv_3_^j^C({a2&RSoVlOn71D8WwMu9@Nb@=E_>1R*ve3`#TF(NA0?d9IR_tm=P zOP-x;gS*vtyE1Cm zG0L?2nRUFj#aLr-R1fX*$sXhad)~xdA*=hF3zPZhha<2O$Ps+F07w*3#MTe?)T8|A!P!v+a|ot{|^$q(TX`35O{WI0RbU zCj?hgOv=Z)xV?F`@HKI11IKtT^ocP78cqHU!YS@cHI@{fPD?YXL)?sD~9thOAv4JM|K8OlQhPXgnevF=F7GKD2#sZW*d za}ma31wLm81IZxX(W#A9mBvLZr|PoLnP>S4BhpK8{YV_}C|p<)4#yO{#ISbco92^3 zv&kCE(q9Wi;9%7>>PQ!zSkM%qqqLZW7O`VXvcj;WcJ`2~v?ZTYB@$Q&^CTfvy?1r^ z;Cdi+PTtmQwHX_7Kz?r#1>D zS5lWU(Mw_$B&`ZPmqxpIvK<~fbXq?x20k1~9az-Q!uR78mCgRj*eQ>zh3c$W}>^+w^dIr-u{@s30J=)1zF8?Wn|H`GS<=>Om|DjzC{}Jt?{!fSJe*@$H zg>wFnlT)k#T?LslW zu$^7Uy~$SQ21cE?3Ijl+bLfuH^U5P^$@~*UY#|_`uvAIe(+wD2eF}z_y!pvomuVO; zS^9fbdv)pcm-B@CW|Upm<7s|0+$@@<&*>$a{aW+oJ%f+VMO<#wa)7n|JL5egEgoBv zl$BY(NQjE0#*nv=!kMnp&{2Le#30b)Ql2e!VkPLK*+{jv77H7)xG7&=aPHL7LK9ER z5lfHxBI5O{-3S?GU4X6$yVk>lFn;ApnwZybdC-GAvaznGW-lScIls-P?Km2mF>%B2 zkcrXTk+__hj-3f48U%|jX9*|Ps41U_cd>2QW81Lz9}%`mTDIhE)jYI$q$ma7Y-`>% z8=u+Oftgcj%~TU}3nP8&h7k+}$D-CCgS~wtWvM|UU77r^pUw3YCV80Ou*+bH0!mf0 zxzUq4ed6y>oYFz7+l18PGGzhB^pqSt)si=9M>~0(Bx9*5r~W7sa#w+_1TSj3Jn9mW zMuG9BxN=}4645Cpa#SVKjFst;9UUY@O<|wpnZk$kE+to^4!?0@?Cwr3(>!NjYbu?x z1!U-?0_O?k!NdM^-rIQ8p)%?M+2xkhltt*|l=%z2WFJhme7*2xD~@zk#`dQR$6Lmd zb3LOD4fdt$Cq>?1<%&Y^wTWX=eHQ49Xl_lFUA(YQYHGHhd}@!VpYHHm=(1-O=yfK#kKe|2Xc*9}?BDFN zD7FJM-AjVi)T~OG)hpSWqH>vlb41V#^G2B_EvYlWhDB{Z;Q9-0)ja(O+By`31=biA zG&Fs#5!%_mHi|E4Nm$;vVQ!*>=_F;ZC=1DTPB#CICS5fL2T3XmzyHu?bI;m7D4@#; ztr~;dGYwb?m^VebuULtS4lkC_7>KCS)F@)0OdxZIFZp@FM_pHnJes8YOvwB|++#G( z&dm*OP^cz95Wi15vh`Q+yB>R{8zqEhz5of>Po$9LNE{xS<)lg2*roP*sQ}3r3t<}; zPbDl{lk{pox~2(XY5=qg0z!W-x^PJ`VVtz$git7?)!h>`91&&hESZy1KCJ2nS^yMH z!=Q$eTyRi68rKxdDsdt+%J_&lapa{ds^HV9Ngp^YDvtq&-Xp}60B_w@Ma>_1TTC;^ zpbe!#gH}#fFLkNo#|`jcn?5LeUYto%==XBk6Ik0kc4$6Z+L3x^4=M6OI1=z5u#M%0 z0E`kevJEpJjvvN>+g`?gtnbo$@p4VumliZV3Z%CfXXB&wPS^5C+7of2tyVkMwNWBiTE2 z8CdPu3i{*vR-I(NY5syRR}I1TJOV@DJy-Xmvxn^IInF>Tx2e)eE9jVSz69$6T`M9-&om!T+I znia!ZWJRB28o_srWlAxtz4VVft8)cYloIoVF=pL zugnk@vFLXQ_^7;%hn9x;Vq?lzg7%CQR^c#S)Oc-8d=q_!2ZVH764V z!wDKSgP}BrVV6SfCLZnYe-7f;igDs9t+K*rbMAKsp9L$Kh<6Z;e7;xxced zn=FGY<}CUz31a2G}$Q(`_r~75PzM4l_({Hg&b@d8&jC}B?2<+ed`f#qMEWi z`gm!STV9E4sLaQX+sp5Nu9*;9g12naf5?=P9p@H@f}dxYprH+3ju)uDFt^V{G0APn zS;16Dk{*fm6&BCg#2vo?7cbkkI4R`S9SSEJ=#KBk3rl69SxnCnS#{*$!^T9UUmO#&XXKjHKBqLdt^3yVvu8yn|{ zZ#%1CP)8t-PAz(+_g?xyq;C2<9<5Yy<~C74Iw(y>uUL$+$mp(DRcCWbCKiGCZw@?_ zdomfp+C5xt;j5L@VfhF*xvZdXwA5pcdsG>G<8II-|1dhAgzS&KArcb0BD4ZZ#WfiEY{hkCq5%z9@f|!EwTm;UEjKJsUo696V>h zy##eXYX}GUu%t{Gql8vVZKkNhQeQ4C%n|RmxL4ee5$cgwlU+?V7a?(jI#&3wid+Kz5+x^G!bb#$q>QpR#BZ}Xo5UW^ zD&I`;?(a}Oys7-`I^|AkN?{XLZNa{@27Dv^s4pGowuyhHuXc zuctKG2x0{WCvg_sGN^n9myJ}&FXyGmUQnW7fR$=bj$AHR88-q$D!*8MNB{YvTTEyS zn22f@WMdvg5~o_2wkjItJN@?mDZ9UUlat2zCh(zVE=dGi$rjXF7&}*sxac^%HFD`Y zTM5D3u5x**{bW!68DL1A!s&$2XG@ytB~dX-?BF9U@XZABO`a|LM1X3HWCllgl0+uL z04S*PX$%|^WAq%jkzp~%9HyYIF{Ym?k)j3nMwPZ=hlCg9!G+t>tf0o|J2%t1 ztC+`((dUplgm3`+0JN~}&FRRJ3?l*>Y&TfjS>!ShS`*MwO{WIbAZR#<%M|4c4^dY8 z{Rh;-!qhY=dz5JthbWoovLY~jNaw>%tS4gHVlt5epV8ekXm#==Po$)}mh^u*cE>q7*kvX&gq)(AHoItMYH6^s6f(deNw%}1=7O~bTHSj1rm2|Cq+3M z93djjdomWCTCYu!3Slx2bZVy#CWDozNedIHbqa|otsUl+ut?>a;}OqPfQA05Yim_2 zs@^BjPoFHOYNc6VbNaR5QZfSMh2S*`BGwcHMM(1@w{-4jVqE8Eu0Bi%d!E*^Rj?cR z7qgxkINXZR)K^=fh{pc0DCKtrydVbVILI>@Y0!Jm>x-xM!gu%dehm?cC6ok_msDVA*J#{75%4IZt}X|tIVPReZS#aCvuHkZxc zHVMtUhT(wp09+w9j9eRqz~LtuSNi2rQx_QgQ(}jBt7NqyT&ma61ldD(s9x%@q~PQl zp6N*?=N$BtvjQ_xIT{+vhb1>{pM0Arde0!X-y))A4znDrVx8yrP3B1(7bKPE5jR@5 zwpzwT4cu~_qUG#zYMZ_!2Tkl9zP>M%cy>9Y(@&VoB84#%>amTAH{(hL4cDYt!^{8L z645F>BWO6QaFJ-{C-i|-d%j7#&7)$X7pv#%9J6da#9FB5KyDhkA+~)G0^87!^}AP>XaCSScr;kL;Z%RSPD2CgoJ;gpYT5&6NUK$86$T?jRH=w8nI9Z534O?5fk{kd z`(-t$8W|#$3>xoMfXvV^-A(Q~$8SKDE^!T;J+rQXP71XZ(kCCbP%bAQ1|%$%Ov9_a zyC`QP3uPvFoBqr_+$HenHklqyIr>PU_Fk5$2C+0eYy^~7U&(!B&&P2%7#mBUhM!z> z_B$Ko?{Pf6?)gpYs~N*y%-3!1>o-4;@1Zz9VQHh)j5U1aL-Hyu@1d?X;jtDBNk*vMXPn@ z+u@wxHN*{uHR!*g*4Xo&w;5A+=Pf9w#PeZ^x@UD?iQ&${K2c}UQgLRik-rKM#Y5rdDphdcNTF~cCX&9ViRP}`>L)QA4zNXeG)KXFzSDa6 zd^St;inY6J_i=5mcGTx4_^Ys`M3l%Q==f>{8S1LEHn{y(kbxn5g1ezt4CELqy)~TV6{;VW>O9?5^ ztcoxHRa0jQY7>wwHWcxA-BCwzsP>63Kt&3fy*n#Cha687CQurXaRQnf5wc9o8v7Rw zNwGr2fac;Wr-Ldehn7tF^(-gPJwPt@VR1f;AmKgxN&YPL;j=0^xKM{!wuU|^mh3NE zy35quf}MeL!PU;|{OW_x$TBothLylT-J>_x6p}B_jW1L>k)ps6n%7Rh z96mPkJIM0QFNYUM2H}YF5bs%@Chs6#pEnloQhEl?J-)es!(SoJpEPoMTdgA14-#mC zghayD-DJWtUu`TD8?4mR)w5E`^EHbsz2EjH5aQLYRcF{l7_Q5?CEEvzDo(zjh|BKg z3aJl_n#j&eFHsUw4~lxqnr!6NL*se)6H=A+T1e3xUJGQrd}oSPwSy5+$tt{2t5J5@(lFxl43amsARG74iyNC}uuS zd2$=(r6RdamdGx^eatX@F2D8?U23tDpR+Os?0Gq2&^dF+$9wiWf?=mDWfjo4LfRwL zI#SRV9iSz>XCSgEj!cW&9H-njJopYiYuq|2w<5R2!nZ27DyvU4UDrHpoNQZiGPkp@ z1$h4H46Zn~eqdj$pWrv;*t!rTYTfZ1_bdkZmVVIRC21YeU$iS-*XMNK`#p8Z_DJx| zk3Jssf^XP7v0X?MWFO{rACltn$^~q(M9rMYoVxG$15N;nP)A98k^m3CJx8>6}NrUd@wp-E#$Q0uUDQT5GoiK_R{ z<{`g;8s>UFLpbga#DAf%qbfi`WN1J@6IA~R!YBT}qp%V-j!ybkR{uY0X|x)gmzE0J z&)=eHPjBxJvrZSOmt|)hC+kIMI;qgOnuL3mbNR0g^<%|>9x7>{}>a2qYSZAGPt4it?8 zNcLc!Gy0>$jaU?}ZWxK78hbhzE+etM`67*-*x4DN>1_&{@5t7_c*n(qz>&K{Y?10s zXsw2&nQev#SUSd|D8w7ZD2>E<%g^; zV{yE_O}gq?Q|zL|jdqB^zcx7vo(^})QW?QKacx$yR zhG|XH|8$vDZNIfuxr-sYFR{^csEI*IM#_gd;9*C+SysUFejP0{{z7@P?1+&_o6=7V|EJLQun^XEMS)w(=@eMi5&bbH*a0f;iC~2J74V2DZIlLUHD&>mlug5+v z6xBN~8-ovZylyH&gG#ptYsNlT?-tzOh%V#Y33zlsJ{AIju`CjIgf$@gr8}JugRq^c zAVQ3;&uGaVlVw}SUSWnTkH_6DISN&k2QLMBe9YU=sA+WiX@z)FoSYX`^k@B!j;ZeC zf&**P?HQG6Rk98hZ*ozn6iS-dG}V>jQhb3?4NJB*2F?6N7Nd;EOOo;xR7acylLaLy z9)^lykX39d@8@I~iEVar4jmjjLWhR0d=EB@%I;FZM$rykBNN~jf>#WbH4U{MqhhF6 zU??@fSO~4EbU4MaeQ_UXQcFyO*Rae|VAPLYMJEU`Q_Q_%s2*>$#S^)&7er+&`9L=1 z4q4ao07Z2Vsa%(nP!kJ590YmvrWg+YrgXYs_lv&B5EcoD`%uL79WyYA$0>>qi6ov7 z%`ia~J^_l{p39EY zv>>b}Qs8vxsu&WcXEt8B#FD%L%ZpcVtY!rqVTHe;$p9rbb5O{^rFMB>auLn-^;s+-&P1#h~mf~YLg$8M9 zZ4#87;e-Y6x6QO<{McUzhy(%*6| z)`D~A(TJ$>+0H+mct(jfgL4x%^oC^T#u(bL)`E2tBI#V1kSikAWmOOYrO~#-cc_8! zCe|@1&mN2{*ceeiBldHCdrURk4>V}79_*TVP3aCyV*5n@jiNbOm+~EQ_}1#->_tI@ zqXv+jj2#8xJtW508rzFrYcJxoek@iW6SR@1%a%Bux&;>25%`j3UI`0DaUr7l79`B1 zqqUARhW1^h6=)6?;@v>xrZNM;t}{yY3P@|L}ey@gG( z9r{}WoYN(9TW&dE2dEJIXkyHA4&pU6ki=rx&l2{DLGbVmg4%3Dlfvn!GB>EVaY_%3+Df{fBiqJV>~Xf8A0aqUjgpa} zoF8YXO&^_x*Ej}nw-$-F@(ddB>%RWoPUj?p8U{t0=n>gAI83y<9Ce@Q#3&(soJ{64 z37@Vij1}5fmzAuIUnXX`EYe;!H-yTVTmhAy;y8VZeB#vD{vw9~P#DiFiKQ|kWwGFZ z=jK;JX*A;Jr{#x?n8XUOLS;C%f|zj-7vXtlf_DtP7bpurBeX%Hjwr z4lI-2TdFpzkjgiv!8Vfv`=SP+s=^i3+N~1ELNWUbH|ytVu>EyPN_3(4TM^QE1swRo zoV7Y_g)a>28+hZG0e7g%@2^s>pzR4^fzR-El}ARTmtu!zjZLuX%>#OoU3}|rFjJg} zQ2TmaygxJ#sbHVyiA5KE+yH0LREWr%^C*yR|@gM$nK2P zo}M}PV0v))uJh&33N>#aU376@ZH79u(Yw`EQ2hM3SJs9f99+cO6_pNW$j$L-CtAfe zYfM)ccwD!P%LiBk!eCD?fHCGvgMQ%Q2oT_gmf?OY=A>&PaZQOq4eT=lwbaf}33LCH zFD|)lu{K7$8n9gX#w4~URjZxWm@wlH%oL#G|I~Fb-v^0L0TWu+`B+ZG!yII)w05DU z>GO?n(TN+B=>HdxVDSlIH76pta$_LhbBg;eZ`M7OGcqt||qi zogS72W1IN%=)5JCyOHWoFP7pOFK0L*OAh=i%&VW&4^LF@R;+K)t^S!96?}^+5QBIs zjJNTCh)?)4k^H^g1&jc>gysM`y^8Rm3qsvkr$9AeWwYpa$b22=yAd1t<*{ zaowSEFP+{y?Ob}8&cwfqoy4Pb9IA~VnM3u!trIK$&&0Op#Ql4j>(EW?UNUv#*iH1$ z^j>+W{afcd`{e&`-A{g}{JnIzYib)!T56IT@YEs{4|`sMpW3c8@UCoIJv`XsAw!XC z34|Il$LpW}CIHFC5e*)}00I5{%OL*WZRGzC0?_}-9{#ue?-ug^ zLE|uv-~6xnSs_2_&CN9{9vyc!Xgtn36_g^wI0C4s0s^;8+p?|mm;Odt3`2ZjwtK;l zfd6j)*Fr#53>C6Y8(N5?$H0ma;BCF3HCjUs7rpb2Kf*x3Xcj#O8mvs#&33i+McX zQpBxD8!O{5Y8D&0*QjD=Yhl9%M0)&_vk}bmN_Ud^BPN;H=U^bn&(csl-pkA+GyY0Z zKV7sU_4n;}uR78ouo8O%g*V;79KY?3d>k6%gpcmQsKk&@Vkw9yna_3asGt`0Hmj59 z%0yiF*`jXhByBI9QsD=+>big5{)BGe&+U2gAARGe3ID)xrid~QN_{I>k}@tzL!Md_ z&=7>TWciblF@EMC3t4-WX{?!m!G6$M$1S?NzF*2KHMP3Go4=#ZHkeIv{eEd;s-yD# z_jU^Ba06TZqvV|Yd;Z_sN%$X=!T+&?#p+OQIHS%!LO`Hx0q_Y0MyGYFNoM{W;&@0@ zLM^!X4KhdtsET5G<0+|q0oqVXMW~-7LW9Bg}=E$YtNh1#1D^6Mz(V9?2g~I1( zoz9Cz=8Hw98zVLwC2AQvp@pBeKyidn6Xu0-1SY1((^Hu*-!HxFUPs)yJ+i`^BC>PC zjwd0mygOVK#d2pRC9LxqGc6;Ui>f{YW9Bvb>33bp^NcnZoH~w9(lM5@JiIlfa-6|k ziy31UoMN%fvQfhi8^T+=yrP{QEyb-jK~>$A4SZT-N56NYEbpvO&yUme&pWKs3^94D zH{oXnUTb3T@H+RgzML*lejx`WAyw*?K7B-I(VJx($2!NXYm%3`=F~TbLv3H<{>D?A zJo-FDYdSA-(Y%;4KUP2SpHKAIcv9-ld(UEJE7=TKp|Gryn;72?0LHqAN^fk6%8PCW z{g_-t)G5uCIf0I`*F0ZNl)Z>))MaLMpXgqWgj-y;R+@A+AzDjsTqw2Mo9ULKA3c70 z!7SOkMtZb+MStH>9MnvNV0G;pwSW9HgP+`tg}e{ij0H6Zt5zJ7iw`hEnvye!XbA@!~#%vIkzowCOvq5I5@$3wtc*w2R$7!$*?}vg4;eDyJ_1=ixJuEp3pUS27W?qq(P^8$_lU!mRChT}ctvZz4p!X^ zOSp|JOAi~f?UkwH#9k{0smZ7-#=lK6X3OFEMl7%)WIcHb=#ZN$L=aD`#DZKOG4p4r zwlQ~XDZ`R-RbF&hZZhu3(67kggsM-F4Y_tI^PH8PMJRcs7NS9ogF+?bZB*fcpJ z=LTM4W=N9yepVvTj&Hu~0?*vR1HgtEvf8w%Q;U0^`2@e8{SwgX5d(cQ|1(!|i$km! zvY03MK}j`sff;*-%mN~ST>xU$6Bu?*Hm%l@0dk;j@%>}jsgDcQ)Hn*UfuThz9(ww_ zasV`rSrp_^bp-0sx>i35FzJwA!d6cZ5#5#nr@GcPEjNnFHIrtUYm1^Z$;{d&{hQV9 z6EfFHaIS}46p^5I-D_EcwwzUUuO}mqRh&T7r9sfw`)G^Q%oHxEs~+XoM?8e*{-&!7 z7$m$lg9t9KP9282eke608^Q2E%H-xm|oJ8=*SyEo} z@&;TQ3K)jgspgKHyGiKVMCz>xmC=H5Fy3!=TP)-R3|&1S-B)!6q50wfLHKM@7Bq6E z44CY%G;GY>tC`~yh!qv~YdXw! zSkquvYNs6k1r7>Eza?Vkkxo6XRS$W7EzL&A`o>=$HXgBp{L(i^$}t`NcnAxzbH8Ht z2!;`bhKIh`f1hIFcI5bHI=ueKdzmB9)!z$s-BT4ItyY|NaA_+o=jO%MU5as9 zc2)aLP>N%u>wlaXTK!p)r?+~)L+0eCGb5{8WIk7K52$nufnQ+m8YF+GQc&{^(zh-$ z#wyWV*Zh@d!b(WwXqvfhQX)^aoHTBkc;4ossV3&Ut*k>AI|m+{#kh4B!`3*<)EJVj zwrxK>99v^k4&Y&`Awm>|exo}NvewV%E+@vOc>5>%H#BK9uaE2$vje zWYM5fKuOTtn96B_2~~!xJPIcXF>E_;yO8AwpJ4)V`Hht#wbO3Ung~@c%%=FX4)q+9 z99#>VC2!4l`~0WHs9FI$Nz+abUq# zz`Of97})Su=^rGp2S$)7N3rQCj#0%2YO<R&p>$<#lgXcUj=4H_{oAYiT3 z44*xDn-$wEzRw7#@6aD)EGO$0{!C5Z^7#yl1o;k0PhN=aVUQu~eTQ^Xy{z8Ow6tk83 z4{5xe%(hx)%nD&|e*6sTWH`4W&U!Jae#U4TnICheJmsw{l|CH?UA{a6?2GNgpZLyzU2UlFu1ZVwlALmh_DOs03J^Cjh1im`E3?9&zvNmg(MuMw&0^Lu$(#CJ*q6DjlKsY-RMJ^8yIY|{SQZ*9~CH|u9L z`R78^r=EbbR*_>5?-)I+$6i}G)%mN(`!X72KaV(MNUP7Nv3MS9S|Pe!%N2AeOt5zG zVJ;jI4HZ$W->Ai_4X+`9c(~m=@ek*m`ZQbv3ryI-AD#AH=`x$~WeW~M{Js57(K7(v ze5`};LG|%C_tmd>bkufMWmAo&B+DT9ZV~h(4jg0>^aeAqL`PEUzJJtI8W1M!bQWpv zvN(d}E1@nlYa!L!!A*RN!(Q3F%J?5PvQ0udu?q-T)j3JKV~NL>KRb~w-lWc685uS6 z=S#aR&B8Sc8>cGJ!!--?kwsJTUUm`Jk?7`H z7PrO~xgBrSW2_tTlCq1LH8*!o?pj?qxy8}(=r_;G18POrFh#;buWR0qU24+XUaVZ0 z?(sXcr@-YqvkCmHr{U2oPogHL{r#3r49TeR<{SJX1pcUqyWPrkYz^X8#QW~?F)R5i z>p^!i<;qM8Nf{-fd6!_&V*e_9qP6q(s<--&1Ttj01j0w>bXY7y1W*%Auu&p|XSOH=)V7Bd4fUKh&T1)@cvqhuD-d=?w}O zjI%i(f|thk0Go*!d7D%0^ztBfE*V=(ZIN84f5HU}T9?ulmEYzT5usi=DeuI*d|;M~ zp_=Cx^!4k#=m_qSPBr5EK~E?3J{dWWPH&oCcNepYVqL?nh4D5ynfWip$m*YlZ8r^Z zuFEUL-nW!3qjRCLIWPT0x)FDL7>Yt7@8dA?R2kF@WE>ysMY+)lTsgNM#3VbXVGL}F z1O(>q>2a+_`6r5Xv$NZAnp=Kgnr3)cL(^=8ypEeOf3q8(HGe@7Tt59;yFl||w|mnO zHDxg2G3z8=(6wjj9kbcEY@Z0iOd7Gq5GiPS5% z*sF1J<#daxDV2Z8H>wxOF<;yKzMeTaSOp_|XkS9Sfn6Mpe9UBi1cSTieGG5$O;ZLIIJ60Y>SN4vC?=yE_CWlo(EEE$e4j?z&^FM%kNmRtlbEL^dPPgvs9sbK5fGw*r@ z+!EU@u$T8!nZh?Fdf_qk$VuHk^yVw`h`_#KoS*N%epIIOfQUy_&V}VWDGp3tplMbf z5Se1sJUC$7N0F1-9jdV2mmGK{-}fu|Nv;12jDy0<-kf^AmkDnu6j~TPWOgy1MT68|D z=4=50jVbUKdKaQgD`eWGr3I&^<6uhkjz$YwItY8%Yp9{z4-{6g{73<_b*@XJ4Nm3-3z z?BW3{aY_ccRjb@W1)i5nLg|7BnWS!B`_Uo9CWaE`Ij327QH?i)9A}4Ug4wmxVVa^b z-4+m%-wwOl7cKH7+=x&nrCrbEC)Q$fpg&V83#uEH;C=GNMz`ps@^RxK%T*8%OPnC` z{WO~J%nxYJ`x|N%?&i7?;{_8t^jM&=50HlaOQj8fS}_`moH$c;vI<|cruPFnpT8yU zS%rPOCUSd5Zdb(zwk`hqwTQn)*&n)uYsP*F_(~xEWq}C= zv30kFmZFwJZ@ELVX3?$dXQh|icO7UrL*_5G=I^xXjImz`ZPp>?g#tf(ej~KaIU0algsG!IS09;>?MvqGg#c{i+}qY|{P8W~O%#>|gFd z<1dr$-oxyRGN17yZo1OwLnzwYs0|;IS_nymNB0IlSzPQ%-r`?T=;_XQ^~&#}b|AB} zkNbN5uB?-sUB-T5QLlg%Uk3)uHB;>VIzGe9_J9 zaeISkQm!v(9d(0ML^b9fR^sfHFlH?7Mvddt37OuR{|O0{uv)(&-6<87W4 zyO>s!=cPgP3O&7xxU5DlIPw_o3O>6o6Qb?JWs3qw#p3sBc3g$?Dx zi(6D+DYgV;GrUis-CL%Qe{nvZnwaVXmbhH(|GFh|Q)k=1uvA$I@1DXI7bKlQ@8D6P zS?(*?><>)G49q0wr;NajpxP4W2G)kHl6^=Z>hrNEI4Mwd_$O6$1dXF;Q#hE(-eeW6 zz03GJF%Wl?HO=_ztv5*zRlcU~{+{k%#N59mgm~eK>P!QZ6E?#Cu^2)+K8m@ySvZ*5 z|HDT}BkF@3!l(0%75G=1u2hETXEj!^1Z$!)!lyGXlWD!_vqGE$Z)#cUVBqlORW>0^ zDjyVTxwKHKG|0}j-`;!R-p>}qQfBl(?($7pP<+Y8QE#M8SCDq~k<+>Q^Zf@cT_WdX3~BSe z+|KK|7OL5Hm5(NFP~j>Ct3*$wi0n0!xl=(C61`q&cec@mFlH(sy%+RH<=s)8aAPN`SfJdkAQjdv82G5iRdv8 zh{9wHUZaniSEpslXl^_ODh}mypC?b*9FzLjb~H@3DFSe;D(A-K3t3eOTB(m~I6C;(-lKAvit(70k`%@+O*Ztdz;}|_TS~B?Tpmi=QKC^m_ z2YpEaT3iiz*;T~ap1yiA)a`dKMwu`^UhIUeltNQ1Yjo=q@bI@&3zH?rVUg=IxLy-ni zyxDu%-Fr{H6owTjZU2O5>nDb=q&Jz_TjeSq%!2m40x&U6w~GQ({quPL73IsJS;f`$ zsuhioqCBj(gJ>2hoo)Gou7(WP*pX)f=Y=!=k!&1K?EYY%jJ~X&DnK{^saPQK<1BJ z_A`_{%ZozcB(3w$z^To^6d|XuT@=X~wtW!+{4ID@N{AB~J6AL5vuY>JwvWCNFKsKh zd}@>q@_WV#QZ&UJ0#?X(pXR!oyXOEG3rqzHbCzGLONDb042i$})fM@XF)uSP(DHUc z^&{|$*xe{cs?Gp8=B%RY3L7#$ve$?TWh>MZdxF1zH1v}1z+$Ov#G7?%D)bBCyDe*% zSeKSpETC2V1){II>@UwJi>4uBN+iAx+82E~gb|Cr&8E^i&)A!uv-g?jzH99wU}8+# z$nh>yvb;TwZmS@7LrvuCu_d0-WxFNI&C7%sWuTL%YU!l|I1{|->=dlOeHOCtUO#zkS3ESO8LHV4hTdQL5EdV zuWD33fFPH}HPrW^s$Qn1Xgp&AT6<-He{{4%eIu3rN=iK|9mURdKXfB&Q?qGok%!cs ze53UP{Z!TO-Y@q2;;k2avA3`lm4OoN4@S*k=UA)7H;qZ`d8`XaYFCv?Ba+uGW@r5v z&&{nf(24WSBOhc7!qF^@0cz;XcUynNaj6w2349;s!K{KVqs5yS{ z7VubS`2OzT^5#1~6Tt^RTvt9-J|D2F>y~>2;jeF>g`hx5l%B3H=aLExQihuYngzlnBTYOTHJQMzl>kwqN5JYs)Ej zblA@ntkUS~xi+}y6|(81helS}Q~&VB37qyV|S3Y=><^1wh%msQM?fz z<58MX(=|PSUKCF#)dbhR%D&xgCD?$aR0qen+wpp6 zst}vX18!Be96TD??j1HsHTUx(a&@F?=gT`Q$oJFFyrh^;zgz!(NlAHGn0cJy@us=w zNhC#l5G;H}+>49Nsh12=ZPO2r*2OBQe5kpb&1?*PIBFitK8}FUfb~S-#hKfF0o#&d z#3aPkB$9scYku&kA6{0xHnBV#&Wei5J>5T-XX-gUXEPo+9b7WL=*XESc(3BshL`aj zXp}QIp*40}oWJt*l043e8_5;H5PI5c)U&IEw5dF(4zjX0y_lk9 zAp@!mK>WUqHo)-jop=DoK>&no>kAD=^qIE7qis&_*4~ z6q^EF$D@R~3_xseCG>Ikb6Gfofb$g|75PPyyZN&tiRxqovo_k zO|HA|sgy#B<32gyU9x^&)H$1jvw@qp+1b(eGAb)O%O!&pyX@^nQd^9BQ4{(F8<}|A zhF&)xusQhtoXOOhic=8#Xtt5&slLia3c*a?dIeczyTbC#>FTfiLST57nc3@Y#v_Eg#VUv zT8cKH#f3=1PNj!Oroz_MAR*pow%Y0*6YCYmUy^7`^r|j23Q~^*TW#cU7CHf0eAD_0 zEWEVddxFgQ7=!nEBQ|ibaScslvhuUk^*%b#QUNrEB{3PG@uTxNwW}Bs4$nS9wc(~O zG7Iq>aMsYkcr!9#A;HNsJrwTDYkK8ikdj{M;N$sN6BqJ<8~z>T20{J8Z2rRUuH7~3 z=tgS`AgxbBOMg87UT4Lwge`*Y=01Dvk>)^{Iu+n6fuVX4%}>?3czOGR$0 zpp*wp>bsFFSV`V;r_m+TZns$ZprIi`OUMhe^cLE$2O+pP3nP!YB$ry}2THx2QJs3< za1;>d-AggCarrQ>&Z!d@;mW+!q6eXhb&`GbzUDSxpl8AJ#Cm#tuc)_xh(2NV=5XMs zrf_ozRYO$NkC=pKFX5OH8v1>0i9Z$ec`~Mf+_jQ68spn(CJwclDhEEkH2Qw;${J$clv__nUjn5jA0wCLEnu1j;v!0vB>Ri6m9`;R{JMS%^)4FC zU0Z44+u$I$w=Bj|iu4DT5h~sS`C*zbmX?@-crY}E+hy>}2~C0Nn(EKk@5^qO4@l@! z6O0lr%tzGC`D^)8xU3FnMZVm0kX1sBWhaQyzVoXFWwr%Ny?=2M{5s#5i7fTu3gEkG zc{(Pr$v=;`Y#&`y*J}#M9ux>0?xu!`$9cUKm#Bdd_&S#LPTS?ZPV6zN6>W6JTS~-LfjL{mB=b(KMk3 z2HjBSlJeyUVqDd=Mt!=hpYsvby2GL&3~zm;0{^nZJq+4vb?5HH4wufvr}IX42sHeK zm@x?HN$8TsTavXs)tLDFJtY9b)y~Tl@7z4^I8oUQq4JckH@~CVQ;FoK(+e0XAM>1O z(ei}h?)JQp>)d=6ng-BZF1Z5hsAKW@mXq+hU?r8I(*%`tnIIOXw7V6ZK(T9RFJJe@ zZS!aC+p)Gf2Ujc=a6hx4!A1Th%YH!Lb^xpI!Eu` zmJO{9rw){B1Ql18d%F%da+Tbu1()?o(zT7StYqK6_w`e+fjXq5L^y(0 z09QA6H4oFj59c2wR~{~>jUoDzDdKz}5#onYPJRwa`SUO)Pd4)?(ENBaFVLJr6Kvz= zhTtXqbx09C1z~~iZt;g^9_2nCZ{};-b4dQJbv8HsWHXPVg^@(*!@xycp#R?a|L!+` zY5w))JWV`Gls(=}shH0#r*;~>_+-P5Qc978+QUd>J%`fyn{*TsiG-dWMiJXNgwBaT zJ=wgYFt+1ACW)XwtNx)Q9tA2LPoB&DkL16P)ERWQlY4%Y`-5aM9mZ{eKPUgI!~J3Z zkMd5A_p&v?V-o-6TUa8BndiX?ooviev(DKw=*bBVOW|=zps9=Yl|-R5@yJe*BPzN}a0mUsLn{4LfjB_oxpv(mwq# zSY*%E{iB)sNvWfzg-B!R!|+x(Q|b@>{-~cFvdDHA{F2sFGA5QGiIWy#3?P2JIpPKg6ncI^)dvqe`_|N=8 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/build-tester/gradlew.bat b/build-tester/gradlew.bat new file mode 100644 index 0000000..db3a6ac --- /dev/null +++ b/build-tester/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/build-tester/key.txt b/build-tester/key.txt new file mode 100644 index 0000000..43fcdf4 --- /dev/null +++ b/build-tester/key.txt @@ -0,0 +1,45 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=E+Fh +-----END PGP PRIVATE KEY BLOCK----- diff --git a/build-tester/notes.txt b/build-tester/notes.txt new file mode 100644 index 0000000..5a08c90 --- /dev/null +++ b/build-tester/notes.txt @@ -0,0 +1,32 @@ +:set BUILD_EVENT=release +set BUILD_EVENT=not_release +set OSSRH_USERNAME=YgqQY2UK +set OSSRH_PASSWORD=1QoJSn6n/vt9dp34vt4GMp1sIJ+4Ed2htekS/KAB15dD +set SIGNING_KEY_ID=01684F29 +set SIGNING_PASSWORD=Zc7+u^Gt8Xumvx3%LyUHX4-p +: set JAVA_HOME=C:\Program Files\Java\jdk-17 +: set PATH=C:\Program Files\Java\jdk-17\bin;%PATH% + + +export BUILD_EVENT=release +export BUILD_EVENT=not_release +export OSSRH_USERNAME=YgqQY2UK +export OSSRH_PASSWORD=1QoJSn6n/vt9dp34vt4GMp1sIJ+4Ed2htekS/KAB15dD +export SIGNING_KEY_ID=01684F29 +export SIGNING_PASSWORD=Zc7+u^Gt8Xumvx3%LyUHX4-p +export SIGNING_KEY="$(cat key.txt)" + + +printenv BUILD_EVENT +printenv OSSRH_USERNAME +printenv OSSRH_PASSWORD +printenv SIGNING_KEY_ID +printenv SIGNING_PASSWORD +printenv SIGNING_KEY + +sudo update-alternatives --config java + +gradle clean test +gradle -i publishToSonatype +gradle -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository +gradle -i publishToSonatype closeAndReleaseSonatypeStagingRepository diff --git a/build-tester/settings.gradle b/build-tester/settings.gradle new file mode 100644 index 0000000..34bf496 --- /dev/null +++ b/build-tester/settings.gradle @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + mavenCentral() + maven { url "https://oss.sonatype.org/content/repositories/releases/" } + maven { url "https://plugins.gradle.org/m2/" } + } +} +rootProject.name = 'build-tester' diff --git a/build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java b/build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java new file mode 100644 index 0000000..2047a8f --- /dev/null +++ b/build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java @@ -0,0 +1,8 @@ +package io.synadia.bt.examples; + +public class BuildTesterExample { + + public static void main(String[] args) { + System.out.println("BuildTesterExample"); + } +} \ No newline at end of file diff --git a/build-tester/src/examples/resources/example.properties b/build-tester/src/examples/resources/example.properties new file mode 100644 index 0000000..e869deb --- /dev/null +++ b/build-tester/src/examples/resources/example.properties @@ -0,0 +1 @@ +io.nats.client.url=nats://localhost:4222 diff --git a/build-tester/src/main/java/io/synadia/bt/BuildTester.java b/build-tester/src/main/java/io/synadia/bt/BuildTester.java new file mode 100644 index 0000000..bc0ca82 --- /dev/null +++ b/build-tester/src/main/java/io/synadia/bt/BuildTester.java @@ -0,0 +1,10 @@ +package io.synadia.bt; + +public class BuildTester { + + public final int x; + + public BuildTester(int x) { + this.x = x; + } +} \ No newline at end of file diff --git a/build-tester/src/main/javadoc/images/favicon.ico b/build-tester/src/main/javadoc/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..430e80fcadbda98f8b8a017b86ed7525744c07bc GIT binary patch literal 15406 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 + + + + +Build Tester. +

    Synadia Logo

    + +Build Tester +

    This package is strictly to test builds.

    + + + + diff --git a/build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java b/build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java new file mode 100644 index 0000000..09b22b6 --- /dev/null +++ b/build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java @@ -0,0 +1,17 @@ +// Copyright (c) 2023-2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.bt; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class BuildTesterTest { + + @Test + public void testBuildTester() throws Exception { + BuildTester bt = new BuildTester(42); + assertEquals(42, bt.x); + } +} diff --git a/build-tester/src/test/resources/test.properties b/build-tester/src/test/resources/test.properties new file mode 100644 index 0000000..e869deb --- /dev/null +++ b/build-tester/src/test/resources/test.properties @@ -0,0 +1 @@ +io.nats.client.url=nats://localhost:4222 From fa8e05092b5e7d41c67e281662ec736930c99049 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 3 Nov 2025 14:15:16 -0500 Subject: [PATCH 080/135] check in --- build-tester/key.txt | 45 -------------------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 build-tester/key.txt diff --git a/build-tester/key.txt b/build-tester/key.txt deleted file mode 100644 index 43fcdf4..0000000 --- a/build-tester/key.txt +++ /dev/null @@ -1,45 +0,0 @@ ------BEGIN PGP PRIVATE KEY BLOCK----- - -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 -=E+Fh ------END PGP PRIVATE KEY BLOCK----- From fc3b5ca97184ebe3231eb4e5260aef4a78207e69 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 3 Nov 2025 15:21:18 -0500 Subject: [PATCH 081/135] check in --- build-tester/notes.txt | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 build-tester/notes.txt diff --git a/build-tester/notes.txt b/build-tester/notes.txt deleted file mode 100644 index 5a08c90..0000000 --- a/build-tester/notes.txt +++ /dev/null @@ -1,32 +0,0 @@ -:set BUILD_EVENT=release -set BUILD_EVENT=not_release -set OSSRH_USERNAME=YgqQY2UK -set OSSRH_PASSWORD=1QoJSn6n/vt9dp34vt4GMp1sIJ+4Ed2htekS/KAB15dD -set SIGNING_KEY_ID=01684F29 -set SIGNING_PASSWORD=Zc7+u^Gt8Xumvx3%LyUHX4-p -: set JAVA_HOME=C:\Program Files\Java\jdk-17 -: set PATH=C:\Program Files\Java\jdk-17\bin;%PATH% - - -export BUILD_EVENT=release -export BUILD_EVENT=not_release -export OSSRH_USERNAME=YgqQY2UK -export OSSRH_PASSWORD=1QoJSn6n/vt9dp34vt4GMp1sIJ+4Ed2htekS/KAB15dD -export SIGNING_KEY_ID=01684F29 -export SIGNING_PASSWORD=Zc7+u^Gt8Xumvx3%LyUHX4-p -export SIGNING_KEY="$(cat key.txt)" - - -printenv BUILD_EVENT -printenv OSSRH_USERNAME -printenv OSSRH_PASSWORD -printenv SIGNING_KEY_ID -printenv SIGNING_PASSWORD -printenv SIGNING_KEY - -sudo update-alternatives --config java - -gradle clean test -gradle -i publishToSonatype -gradle -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository -gradle -i publishToSonatype closeAndReleaseSonatypeStagingRepository From 1cf7be2f8b1cde5c5999c3589d57dd7a09f925ef Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 11 Nov 2025 19:24:57 -0500 Subject: [PATCH 082/135] Basic Usage --- counters/README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/counters/README.md b/counters/README.md index f2dc2f3..2cff954 100644 --- a/counters/README.md +++ b/counters/README.md @@ -11,6 +11,73 @@ https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md   **Gradle and Maven** `io.synadia:counters` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) +## Basic Usage + +```java +Options options = ... +try (Connection nc = Nats.connect(options)) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // setup the coutner stream + Counters counters = createCountersStream(nc, + StreamConfiguration.builder() + .name("counters-stream") + .subjects("cs.*") + .storageType(StorageType.Memory) + .build()); + + // add + BigInteger bi = counters.add("cs.A", 1); + bi = counters.add("cs.A", 2); + + bi = counters.add("cs.B", 10); + bi = counters.add("cs.B", 20); + + // get + bi = counters.get("cs.A"); + bi = counters.get("cs.B"); +``` + + +## API + +JetStreamOptions are necessary for stream creation and instance construction if your stream needs a prefix or domain. + +### Create Counter Stream +```java +public static Counters createCountersStream(Connection conn, StreamConfiguration userConfig) throws JetStreamApiException, IOException +public static Counters createCountersStream(Connection conn, JetStreamOptions jso, StreamConfiguration userConfig) throws JetStreamApiException, IOException +``` + +### Create Counters Instance +You get a counters instance on construction of the stream as above or by constructing an instance directly. + +```java +public Counters(String streamName, Connection conn) throws IOException, JetStreamApiException +public Counters(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException +``` + +### Use Counter +```java +public BigInteger add(String subject, int value) throws JetStreamApiException, IOException +public BigInteger add(String subject, long value) throws JetStreamApiException, IOException +public BigInteger add(String subject, BigInteger value) throws JetStreamApiException, IOException +public BigInteger increment(String subject) throws JetStreamApiException, IOException +public BigInteger decrement(String subject) throws JetStreamApiException, IOException +public BigInteger setViaAdd(String subject, int value) throws JetStreamApiException, IOException +public BigInteger setViaAdd(String subject, long value) throws JetStreamApiException, IOException +public BigInteger setViaAdd(String subject, BigInteger value) throws JetStreamApiException, IOException +public BigInteger get(String subject) throws JetStreamApiException, IOException +public BigInteger getOrElse(String subject, int dflt) throws IOException +public BigInteger getOrElse(String subject, long dflt) throws IOException +public BigInteger getOrElse(String subject, BigInteger dflt) throws IOException +public CounterEntry getEntry(String subject) throws JetStreamApiException, IOException +public LinkedBlockingQueue getEntries(String... subjects) +public LinkedBlockingQueue getEntries(List subjects) +public CounterIterator iterateEntries(String... subjects) +public CounterIterator iterateEntries(List subjects) +public CounterIterator iterateEntries(List subjects, Duration timeoutFirst, Duration timeoutSubsequent) +``` ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-00BC8E?labelColor=grey&style=flat) [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters) From 9c171cfe67615f563c5456568100d2e5faf28e64 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 11 Nov 2025 19:26:30 -0500 Subject: [PATCH 083/135] Basic Usage --- counters/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counters/README.md b/counters/README.md index 2cff954..90d656c 100644 --- a/counters/README.md +++ b/counters/README.md @@ -57,7 +57,7 @@ public Counters(String streamName, Connection conn) throws IOException, JetStrea public Counters(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException ``` -### Use Counter +### Counters instance API ```java public BigInteger add(String subject, int value) throws JetStreamApiException, IOException public BigInteger add(String subject, long value) throws JetStreamApiException, IOException From 7907ce39e2aaf20dba2e04710e6e04f968f76f32 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 18 Nov 2025 11:12:57 -0500 Subject: [PATCH 084/135] update jnats version --- batch-publish/build.gradle | 2 +- chaos-runner/build.gradle | 2 +- direct-batch/build.gradle | 2 +- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- schedule-message/build.gradle | 2 +- utils/build.gradle | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index 806bfc8..e4d72ff 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 15fc83b..5ba45fd 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' // this is only for the example and the uber jar won't include it - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 3570da7..994ebf4 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index 244ad56..49c9975 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index 316630a..6ff9cbf 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index be35fcd..febb858 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index bc5b905..d023c4c 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 152be51..3d242b7 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'commons-codec:commons-codec:1.18.0' diff --git a/utils/build.gradle b/utils/build.gradle index 457fedc..8c8eb5f 100644 --- a/utils/build.gradle +++ b/utils/build.gradle @@ -22,5 +22,5 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' } From 981d88099ad87c3f95ed4eb0bb95c40d767ab8ac Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 18 Nov 2025 11:37:21 -0500 Subject: [PATCH 085/135] update tester project --- build-tester/build.gradle | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/build-tester/build.gradle b/build-tester/build.gradle index 2acf10c..83313f0 100644 --- a/build-tester/build.gradle +++ b/build-tester/build.gradle @@ -1,5 +1,4 @@ import aQute.bnd.gradle.Bundle -import org.gradle.internal.os.OperatingSystem plugins { id("java") @@ -18,12 +17,16 @@ group = 'io.synadia' def isRelease = System.getenv("BUILD_EVENT") == "release" -// version is the variable the build actually uses. -version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" +def tc = System.getenv("TARGET_COMPATIBILITY"); +def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) +def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") +def jarAndArtifactName = "build-tester" + jarEnd + +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. java { sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = targetCompat } // java { @@ -40,7 +43,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.5' + implementation 'io.nats:jnats:2.24.0' implementation 'org.jspecify:jspecify:1.0.0' implementation 'org.apache.commons:commons-lang3:3.18.0' @@ -107,21 +110,6 @@ javadoc { title = "Synadia Communications Inc. Build Tester" excludes ['**/examples/**'] classpath = sourceSets.main.runtimeClasspath - doLast { - if (!OperatingSystem.current().isWindows()) { - exec { - println "Updating favicon on all html files" - workingDir 'build/docs/javadoc' - // Only on linux, mac at this point - commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' - } - copy { - println "Copying images to javadoc folder" - from 'src/main/javadoc/images' - into 'build/docs/javadoc' - } - } - } } tasks.register('examplesJar', Jar) { @@ -151,6 +139,10 @@ tasks.register('testsJar', Jar) { from sourceSets.test.allSource } +artifacts { + archives javadocJar, sourcesJar, examplesJar, testsJar +} + jacoco { toolVersion = "0.8.12" } @@ -168,10 +160,6 @@ jacocoTestReport { } } -artifacts { - archives javadocJar, sourcesJar, examplesJar, testsJar -} - nexusPublishing { repositories { sonatype { From 28f93809630ff55752c26053ddd0846100e9c879 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 18 Nov 2025 11:40:43 -0500 Subject: [PATCH 086/135] Upgrade Gradle * Gradle 8.14 * Java 21 as the build JDK (not the target) --- .github/workflows/counters-main.yml | 21 +- .github/workflows/counters-pr.yml | 23 +- .github/workflows/counters-release.yml | 23 +- counters/build.gradle | 106 +++---- counters/gradle/wrapper/gradle-wrapper.jar | Bin 58694 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- counters/gradlew | 286 +++++++++++------- counters/gradlew.bat | 59 ++-- counters/settings.gradle | 21 +- 9 files changed, 308 insertions(+), 235 deletions(-) diff --git a/.github/workflows/counters-main.yml b/.github/workflows/counters-main.yml index 483dbc6..715cec7 100644 --- a/.github/workflows/counters-main.yml +++ b/.github/workflows/counters-main.yml @@ -18,18 +18,22 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compile and Test run: | pushd counters @@ -45,3 +49,6 @@ jobs: pushd counters ./gradlew -i publishToSonatype popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/counters-pr.yml b/.github/workflows/counters-pr.yml index 2fe5625..1a02adc 100644 --- a/.github/workflows/counters-pr.yml +++ b/.github/workflows/counters-pr.yml @@ -17,19 +17,23 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 - - name: Compile and Test + uses: actions/checkout@v4 + - name: Build and Test run: | pushd counters chmod +x gradlew && ./gradlew clean test @@ -39,3 +43,6 @@ jobs: pushd counters ./gradlew javadoc popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/counters-release.yml b/.github/workflows/counters-release.yml index f6659da..dc24cf5 100644 --- a/.github/workflows/counters-release.yml +++ b/.github/workflows/counters-release.yml @@ -15,18 +15,22 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compile and Test run: | pushd counters @@ -35,5 +39,8 @@ jobs: - name: Verify, Sign and Publish Release run: | pushd counters - ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + ./gradlew -i publishToSonatype closeAndReleaseSonatypeStagingRepository popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/counters/build.gradle b/counters/build.gradle index 1243908..9f0e6b6 100644 --- a/counters/build.gradle +++ b/counters/build.gradle @@ -1,56 +1,57 @@ import aQute.bnd.gradle.Bundle -import org.gradle.internal.os.OperatingSystem plugins { - id 'java' - id 'java-library' - id 'maven-publish' - id 'jacoco' - id 'com.github.kt3k.coveralls' version '2.12.0' - id 'biz.aQute.bnd.builder' version '5.1.2' - id "org.gradle.test-retry" version "1.1.9" - id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' - id 'signing' + id("java") + id("java-library") + id("maven-publish") + id("jacoco") + id("biz.aQute.bnd.builder") version "7.1.0" + id("org.gradle.test-retry") version "1.6.4" + id("io.github.gradle-nexus.publish-plugin") version "2.0.0" + id("signing") } def jarVersion = "0.2.1" group = 'io.synadia' -def isMerge = System.getenv("BUILD_EVENT") == "push" def isRelease = System.getenv("BUILD_EVENT") == "release" -// version is the variable the build actually uses. -version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" +def tc = System.getenv("TARGET_COMPATIBILITY"); +def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) +def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") +def jarAndArtifactName = "counters" + jarEnd + +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. + + +System.out.println("Java: " + System.getProperty("java.version")) +System.out.println("Target Compatibility: " + targetCompat) +System.out.println(group + ":" + jarAndArtifactName + ":" + version) java { sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = targetCompat } repositories { mavenCentral() - maven { - url "https://repo1.maven.org/maven2/" - } - maven { - url "https://central.sonatype.com/repository/maven-snapshots/" - } + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } } dependencies { - implementation 'io.nats:jnats:2.23.0' + implementation 'io.nats:jnats:2.24.0' implementation 'io.synadia:direct-batch:0.1.4' implementation 'org.jspecify:jspecify:1.0.0' - testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + + testImplementation 'commons-codec:commons-codec:1.20.0' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' - testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' -} + testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.3' -configurations.configureEach { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } sourceSets { @@ -58,6 +59,9 @@ sourceSets { java { srcDirs = ['src/main/java','src/examples/java'] } + resources { + srcDirs = ['src/examples/resources'] + } } test { java { @@ -72,18 +76,17 @@ tasks.register('bundle', Bundle) { } jar { - manifest { - attributes('Automatic-Module-Name': 'io.synadia.counters') + bundle { + bnd("Bundle-Name": "io.synadia.counters", + "Bundle-Vendor": "synadia.io", + "Bundle-Description": "JetStream Distributed Counters", + "Bundle-DocURL": "https://github.com/synadia-io/orbit.java/tree/main/counters", + "Target-Compatibility": "Java " + targetCompat + ) } - bnd (['Implementation-Title': 'JetStream Distributed Counters', - 'Implementation-Version': jarVersion, - 'Implementation-Vendor': 'synadia.io'] - ) - exclude("io/synadia/examples/**") } test { - // Use junit platform for unit tests useJUnitPlatform() } @@ -92,21 +95,6 @@ javadoc { source = sourceSets.main.allJava title = "Synadia Communications Inc. JetStream Distributed Counters" classpath = sourceSets.main.runtimeClasspath - doLast { - if (!OperatingSystem.current().isWindows()) { - exec { - println "Updating favicon on all html files" - workingDir 'build/docs/javadoc' - // Only on linux, mac at this point - commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' - } - copy { - println "Copying images to javadoc folder" - from 'src/main/javadoc/images' - into 'build/docs/javadoc' - } - } - } } tasks.register('examplesJar', Jar) { @@ -131,14 +119,18 @@ tasks.register('sourcesJar', Jar) { from sourceSets.main.allSource } +artifacts { + archives javadocJar, sourcesJar, examplesJar +} + jacoco { - toolVersion = "0.8.6" + toolVersion = "0.8.12" } jacocoTestReport { reports { - xml.enabled = true // coveralls plugin depends on xml format report - html.enabled = true + xml.required = true // coveralls plugin depends on xml format report + html.required = true } afterEvaluate { // only report on main library not examples classDirectories.setFrom(files(classDirectories.files.collect { @@ -148,10 +140,6 @@ jacocoTestReport { } } -artifacts { - archives javadocJar, sourcesJar, examplesJar -} - nexusPublishing { repositories { sonatype { @@ -171,10 +159,10 @@ publishing { artifact examplesJar artifact javadocJar pom { - name = 'counters' + name = jarAndArtifactName packaging = 'jar' groupId = group - artifactId = archivesBaseName + artifactId = jarAndArtifactName description = 'Synadia Communications Inc. JetStream Distributed Counters' url = 'https://github.com/synadia-io/orbit.java' licenses { diff --git a/counters/gradle/wrapper/gradle-wrapper.jar b/counters/gradle/wrapper/gradle-wrapper.jar index 490fda8577df6c95960ba7077c43220e5bb2c0d9..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch literal 43764 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 diff --git a/counters/gradle/wrapper/gradle-wrapper.properties b/counters/gradle/wrapper/gradle-wrapper.properties index 442d913..ca025c8 100644 --- a/counters/gradle/wrapper/gradle-wrapper.properties +++ b/counters/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/counters/gradlew b/counters/gradlew index 2fe81a7..23d15a9 100644 --- a/counters/gradlew +++ b/counters/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,80 +15,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,87 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/counters/gradlew.bat b/counters/gradlew.bat index 62bd9b9..db3a6ac 100644 --- a/counters/gradlew.bat +++ b/counters/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -54,48 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/counters/settings.gradle b/counters/settings.gradle index 46d185c..78be690 100644 --- a/counters/settings.gradle +++ b/counters/settings.gradle @@ -1,10 +1,13 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html - */ - +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://plugins.gradle.org/m2/" } + } + plugins { + id("biz.aQute.bnd.builder") version "7.1.0" + } +} rootProject.name = 'counters' From fc081c46e202f9929935ff56cd843ef5f66b9796 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 18 Nov 2025 17:34:01 -0500 Subject: [PATCH 087/135] Start 0.2.2 --- counters/README.md | 4 ++-- counters/build.gradle | 4 +--- counters/gradle/libs.versions.toml | 12 ++++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 counters/gradle/libs.versions.toml diff --git a/counters/README.md b/counters/README.md index 90d656c..b343ba1 100644 --- a/counters/README.md +++ b/counters/README.md @@ -6,8 +6,8 @@ Utility to take advantage of the distributed counter functionality. https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md -**Current Release**: 0.2.0 -  **Current Snapshot**: 0.2.1-SNAPSHOT +**Current Release**: 0.2.1 +  **Current Snapshot**: 0.2.2-SNAPSHOT   **Gradle and Maven** `io.synadia:counters` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/counters/build.gradle b/counters/build.gradle index 9f0e6b6..22b11e4 100644 --- a/counters/build.gradle +++ b/counters/build.gradle @@ -11,7 +11,7 @@ plugins { id("signing") } -def jarVersion = "0.2.1" +def jarVersion = "0.2.2" group = 'io.synadia' def isRelease = System.getenv("BUILD_EVENT") == "release" @@ -23,7 +23,6 @@ def jarAndArtifactName = "counters" + jarEnd version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. - System.out.println("Java: " + System.getProperty("java.version")) System.out.println("Target Compatibility: " + targetCompat) System.out.println(group + ":" + jarAndArtifactName + ":" + version) @@ -45,7 +44,6 @@ dependencies { implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' - testImplementation 'commons-codec:commons-codec:1.20.0' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.3' diff --git a/counters/gradle/libs.versions.toml b/counters/gradle/libs.versions.toml new file mode 100644 index 0000000..2cfe86a --- /dev/null +++ b/counters/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.4.5-jre" +junit-jupiter = "5.12.1" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } From 3fd106ca52bfe66b58f7fd5dd29eec629eb84211 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 18 Nov 2025 17:34:57 -0500 Subject: [PATCH 088/135] Update Gradle Version --- utils/build.gradle | 16 +- utils/gradle/libs.versions.toml | 12 + utils/gradle/wrapper/gradle-wrapper.jar | Bin 58694 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- utils/gradlew | 286 +++++++++++------- utils/gradlew.bat | 59 ++-- utils/settings.gradle | 21 +- 7 files changed, 238 insertions(+), 160 deletions(-) create mode 100644 utils/gradle/libs.versions.toml diff --git a/utils/build.gradle b/utils/build.gradle index 8c8eb5f..406a587 100644 --- a/utils/build.gradle +++ b/utils/build.gradle @@ -6,19 +6,21 @@ plugins { group = 'io.synadia' version = "0.0.0" +def tc = System.getenv("TARGET_COMPATIBILITY"); +def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) + +System.out.println("Java: " + System.getProperty("java.version")) +System.out.println("Target Compatibility: " + targetCompat) + java { sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = targetCompat } repositories { mavenCentral() - maven { - url "https://repo1.maven.org/maven2/" - } - maven { - url "https://central.sonatype.com/repository/maven-snapshots/" - } + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } } dependencies { diff --git a/utils/gradle/libs.versions.toml b/utils/gradle/libs.versions.toml new file mode 100644 index 0000000..2cfe86a --- /dev/null +++ b/utils/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.4.5-jre" +junit-jupiter = "5.12.1" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/utils/gradle/wrapper/gradle-wrapper.jar b/utils/gradle/wrapper/gradle-wrapper.jar index 490fda8577df6c95960ba7077c43220e5bb2c0d9..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch literal 43764 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 diff --git a/utils/gradle/wrapper/gradle-wrapper.properties b/utils/gradle/wrapper/gradle-wrapper.properties index 442d913..ca025c8 100644 --- a/utils/gradle/wrapper/gradle-wrapper.properties +++ b/utils/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/utils/gradlew b/utils/gradlew index 2fe81a7..23d15a9 100644 --- a/utils/gradlew +++ b/utils/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,80 +15,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,87 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/utils/gradlew.bat b/utils/gradlew.bat index 62bd9b9..db3a6ac 100644 --- a/utils/gradlew.bat +++ b/utils/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -54,48 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/utils/settings.gradle b/utils/settings.gradle index e9f369e..f9dbf60 100644 --- a/utils/settings.gradle +++ b/utils/settings.gradle @@ -1,10 +1,13 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html - */ - +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://plugins.gradle.org/m2/" } + } + plugins { + id("biz.aQute.bnd.builder") version "7.1.0" + } +} rootProject.name = 'utils' From 3e1346f74708be62acbb80bb6514bb0394391524 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 18 Nov 2025 17:36:01 -0500 Subject: [PATCH 089/135] Upgrade Gradle * Gradle 8.14 * Java 21 as the build JDK (not the target) --- .github/workflows/bp-main.yml | 21 +- .github/workflows/bp-pr.yml | 23 +- .github/workflows/bp-release.yml | 23 +- batch-publish/build.gradle | 96 +++--- batch-publish/gradle/libs.versions.toml | 12 + .../gradle/wrapper/gradle-wrapper.jar | Bin 58694 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- batch-publish/gradlew | 286 +++++++++++------- batch-publish/gradlew.bat | 59 ++-- batch-publish/settings.gradle | 21 +- 10 files changed, 313 insertions(+), 232 deletions(-) create mode 100644 batch-publish/gradle/libs.versions.toml diff --git a/.github/workflows/bp-main.yml b/.github/workflows/bp-main.yml index 8aff27c..d3edea3 100644 --- a/.github/workflows/bp-main.yml +++ b/.github/workflows/bp-main.yml @@ -18,18 +18,22 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compile and Test run: | pushd batch-publish @@ -45,3 +49,6 @@ jobs: pushd batch-publish ./gradlew -i publishToSonatype popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/bp-pr.yml b/.github/workflows/bp-pr.yml index 8a0f9f5..0e5eb97 100644 --- a/.github/workflows/bp-pr.yml +++ b/.github/workflows/bp-pr.yml @@ -17,19 +17,23 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 - - name: Compile and Test + uses: actions/checkout@v4 + - name: Build and Test run: | pushd batch-publish chmod +x gradlew && ./gradlew clean test @@ -39,3 +43,6 @@ jobs: pushd batch-publish ./gradlew javadoc popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/bp-release.yml b/.github/workflows/bp-release.yml index 5bb780d..f4d192f 100644 --- a/.github/workflows/bp-release.yml +++ b/.github/workflows/bp-release.yml @@ -15,18 +15,22 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compile and Test run: | pushd batch-publish @@ -35,5 +39,8 @@ jobs: - name: Verify, Sign and Publish Release run: | pushd batch-publish - ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + ./gradlew -i publishToSonatype closeAndReleaseSonatypeStagingRepository popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index e4d72ff..1a7791d 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -1,40 +1,41 @@ import aQute.bnd.gradle.Bundle -import org.gradle.internal.os.OperatingSystem plugins { - id 'java' - id 'java-library' - id 'maven-publish' - id 'jacoco' - id 'com.github.kt3k.coveralls' version '2.12.0' - id 'biz.aQute.bnd.builder' version '5.1.2' - id "org.gradle.test-retry" version "1.1.9" - id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' - id 'signing' + id("java") + id("java-library") + id("maven-publish") + id("jacoco") + id("biz.aQute.bnd.builder") version "7.1.0" + id("org.gradle.test-retry") version "1.6.4" + id("io.github.gradle-nexus.publish-plugin") version "2.0.0" + id("signing") } def jarVersion = "0.2.3" group = 'io.synadia' -def isMerge = System.getenv("BUILD_EVENT") == "push" def isRelease = System.getenv("BUILD_EVENT") == "release" -// version is the variable the build actually uses. -version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" +def tc = System.getenv("TARGET_COMPATIBILITY"); +def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) +def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") +def jarAndArtifactName = "batch-publish" + jarEnd + +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. + +System.out.println("Java: " + System.getProperty("java.version")) +System.out.println("Target Compatibility: " + targetCompat) +System.out.println(group + ":" + jarAndArtifactName + ":" + version) java { sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = targetCompat } repositories { mavenCentral() - maven { - url "https://repo1.maven.org/maven2/" - } - maven { - url "https://central.sonatype.com/repository/maven-snapshots/" - } + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } } dependencies { @@ -42,13 +43,11 @@ dependencies { implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' - testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' -} + testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.3' -configurations.configureEach { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } sourceSets { @@ -70,14 +69,14 @@ tasks.register('bundle', Bundle) { } jar { - manifest { - attributes('Automatic-Module-Name': 'io.synadia.batch.publish') + bundle { + bnd("Bundle-Name": "io.synadia.batch.publish", + "Bundle-Vendor": "synadia.io", + "Bundle-Description": "JetStream Distributed Counters", + "Bundle-DocURL": "https://github.com/synadia-io/orbit.java/tree/main/counters", + "Target-Compatibility": "Java " + targetCompat + ) } - bnd (['Implementation-Title': 'Batch Publish', - 'Implementation-Version': jarVersion, - 'Implementation-Vendor': 'synadia.io'] - ) - exclude("io/synadia/examples/**") } test { @@ -90,21 +89,6 @@ javadoc { source = sourceSets.main.allJava title = "Synadia Communications Inc. Batch Publish" classpath = sourceSets.main.runtimeClasspath - doLast { - if (!OperatingSystem.current().isWindows()) { - exec { - println "Updating favicon on all html files" - workingDir 'build/docs/javadoc' - // Only on linux, mac at this point - commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' - } - copy { - println "Copying images to javadoc folder" - from 'src/main/javadoc/images' - into 'build/docs/javadoc' - } - } - } } tasks.register('examplesJar', Jar) { @@ -129,14 +113,18 @@ tasks.register('sourcesJar', Jar) { from sourceSets.main.allSource } +artifacts { + archives javadocJar, sourcesJar, examplesJar +} + jacoco { - toolVersion = "0.8.6" + toolVersion = "0.8.12" } jacocoTestReport { reports { - xml.enabled = true // coveralls plugin depends on xml format report - html.enabled = true + xml.required = true // coveralls plugin depends on xml format report + html.required = true } afterEvaluate { // only report on main library not examples classDirectories.setFrom(files(classDirectories.files.collect { @@ -146,10 +134,6 @@ jacocoTestReport { } } -artifacts { - archives javadocJar, sourcesJar, examplesJar -} - nexusPublishing { repositories { sonatype { @@ -169,10 +153,10 @@ publishing { artifact examplesJar artifact javadocJar pom { - name = 'batch-publish' + name = jarAndArtifactName packaging = 'jar' groupId = group - artifactId = archivesBaseName + artifactId = jarAndArtifactName description = 'Synadia Communications Inc. Batch Publish' url = 'https://github.com/synadia-io/orbit.java' licenses { diff --git a/batch-publish/gradle/libs.versions.toml b/batch-publish/gradle/libs.versions.toml new file mode 100644 index 0000000..2cfe86a --- /dev/null +++ b/batch-publish/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.4.5-jre" +junit-jupiter = "5.12.1" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/batch-publish/gradle/wrapper/gradle-wrapper.jar b/batch-publish/gradle/wrapper/gradle-wrapper.jar index 490fda8577df6c95960ba7077c43220e5bb2c0d9..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch literal 43764 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 diff --git a/batch-publish/gradle/wrapper/gradle-wrapper.properties b/batch-publish/gradle/wrapper/gradle-wrapper.properties index 442d913..ca025c8 100644 --- a/batch-publish/gradle/wrapper/gradle-wrapper.properties +++ b/batch-publish/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/batch-publish/gradlew b/batch-publish/gradlew index 2fe81a7..23d15a9 100644 --- a/batch-publish/gradlew +++ b/batch-publish/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,80 +15,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,87 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/batch-publish/gradlew.bat b/batch-publish/gradlew.bat index 62bd9b9..db3a6ac 100644 --- a/batch-publish/gradlew.bat +++ b/batch-publish/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -54,48 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/batch-publish/settings.gradle b/batch-publish/settings.gradle index 450b14f..afe3449 100644 --- a/batch-publish/settings.gradle +++ b/batch-publish/settings.gradle @@ -1,10 +1,13 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html - */ - +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://plugins.gradle.org/m2/" } + } + plugins { + id("biz.aQute.bnd.builder") version "7.1.0" + } +} rootProject.name = 'batch-publish' From 455f19260c539613af038da8f63ede0f27d8acae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Moyne?= Date: Fri, 20 Feb 2026 01:11:22 +0100 Subject: [PATCH 090/135] Initial commit for the port of Partitioned Consumer Groups from https://github.com/synadia-io/orbit.go/tree/main/pcgroups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-Noël Moyne --- README.md | 32 +- pcgroups/README.md | 103 ++ pcgroups/cli/pom.xml | 132 +++ .../java/io/synadia/pcg/cli/CgCommand.java | 59 ++ .../java/io/synadia/pcg/cli/CliUtils.java | 266 +++++ .../io/synadia/pcg/cli/DurationConverter.java | 110 ++ .../io/synadia/pcg/cli/ElasticCommands.java | 435 ++++++++ .../io/synadia/pcg/cli/PromptHandler.java | 688 +++++++++++++ .../io/synadia/pcg/cli/StaticCommands.java | 363 +++++++ pcgroups/pom.xml | 91 ++ pcgroups/scripts/elastic_stream_setup.sh | 2 + pcgroups/scripts/generate_traffic.sh | 2 + pcgroups/scripts/static_stream_setup.sh | 2 + .../pcg/ConsumerGroupConsumeContext.java | 33 + .../java/io/synadia/pcg/ConsumerGroupMsg.java | 140 +++ .../io/synadia/pcg/ElasticConsumerGroup.java | 968 ++++++++++++++++++ .../pcg/ElasticConsumerGroupConfig.java | 293 ++++++ .../java/io/synadia/pcg/MemberMapping.java | 78 ++ .../java/io/synadia/pcg/PartitionUtils.java | 169 +++ .../io/synadia/pcg/StaticConsumerGroup.java | 508 +++++++++ .../pcg/StaticConsumerGroupConfig.java | 177 ++++ .../exceptions/ConsumerGroupException.java | 32 + .../synadia/pcg/ElasticConsumerGroupTest.java | 390 +++++++ .../java/io/synadia/pcg/IntegrationTest.java | 238 +++++ .../io/synadia/pcg/PartitionUtilsTest.java | 272 +++++ .../synadia/pcg/StaticConsumerGroupTest.java | 305 ++++++ 26 files changed, 5877 insertions(+), 11 deletions(-) create mode 100644 pcgroups/README.md create mode 100644 pcgroups/cli/pom.xml create mode 100644 pcgroups/cli/src/main/java/io/synadia/pcg/cli/CgCommand.java create mode 100644 pcgroups/cli/src/main/java/io/synadia/pcg/cli/CliUtils.java create mode 100644 pcgroups/cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java create mode 100644 pcgroups/cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java create mode 100644 pcgroups/cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java create mode 100644 pcgroups/cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java create mode 100644 pcgroups/pom.xml create mode 100755 pcgroups/scripts/elastic_stream_setup.sh create mode 100755 pcgroups/scripts/generate_traffic.sh create mode 100755 pcgroups/scripts/static_stream_setup.sh create mode 100644 pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupConsumeContext.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupMsg.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java create mode 100644 pcgroups/src/main/java/io/synadia/pcg/exceptions/ConsumerGroupException.java create mode 100644 pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java create mode 100644 pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java create mode 100644 pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java create mode 100644 pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java diff --git a/README.md b/README.md index 40c8271..0effd8c 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,18 @@ boost productivity and provide a higher abstraction layer for the [JNATS](https: client. Note that these libraries will evolve rapidly and API guarantees are general not made until the specific project has a v1.0.0 version. # Utilities -| Module | Description | Docs | Release Version | Snapshot | -|------------------------------|------------------------------------------------------|-----------------------------------------------|-----------------|----------------| -| Retrier | Extension for retrying anything | [README.md](retrier/README.md) | 0.2.1 | 0.2.2-SNAPSHOT | -| Jetstream Publish Extensions | General extensions for Jetstream Publishing | [README.md](js-publish-extensions/README.md) | 0.4.4 | 0.4.5-SNAPSHOT | -| Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | -| Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | -| Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | -| Batch Publish | Publish an atomic batch | [README.md](dirrrrrrr/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | -| Distributed Counters | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.2.0 | 0.2.1-SNAPSHOT | -| Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.2 | 0.0.3-SNAPSHOT | -| Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | +| Module | Description | Docs | Release Version | Snapshot | +|------------------------------|---------------------------------------------------------|----------------------------------------------|-----------------|----------------| +| Retrier | Extension for retrying anything | [README.md](retrier/README.md) | 0.2.1 | 0.2.2-SNAPSHOT | +| Jetstream Publish Extensions | General extensions for Jetstream Publishing | [README.md](js-publish-extensions/README.md) | 0.4.4 | 0.4.5-SNAPSHOT | +| Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | +| Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | +| Batch Publish | Publish an atomic batch | [README.md](dirrrrrrr/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | +| Distributed Counters | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.2.0 | 0.2.1-SNAPSHOT | +| Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.2 | 0.0.3-SNAPSHOT | +| Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | +| Partitioned Consumer Groups | Partitioned Consumer Group funcitionality for JetStream | [README.md](pcgroups/README.md) | 0.0.1 | 0.0.1-SNAPSHOT | ## Retrier @@ -129,6 +130,15 @@ Run some NATS servers and cause chaos by bringing them up and down. [![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner) +## Partitioned Consumer Groups + +Implementation of the partitioned Consumer Group functionality, ported from and compatible with the [Golang version](https://github.com/synadia-io/orbit.go/tree/main/pcgroups). + +**Current Release**: 0.0.1 +  **Current Snapshot**: 0.0.1-SNAPSHOT + +[![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](pcgroups/README.md) + # Dependencies ### Gradle diff --git a/pcgroups/README.md b/pcgroups/README.md new file mode 100644 index 0000000..8eba332 --- /dev/null +++ b/pcgroups/README.md @@ -0,0 +1,103 @@ +# Partitioned Consumer Groups + +[License-Url]: https://www.apache.org/licenses/LICENSE-2.0 +[License-Image]: https://img.shields.io/badge/License-Apache2-blue.svg + +[![License][License-Image]][License-Url] + +Initial implementation of a client-side partitioned consumer group feature for NATS streams leveraging some of the new features introduced in `nats-server` version 2.11. + +Note that post 2.11 versions of `nats-server` may include new features related to the consumer group use case that could render this client-side library unneeded (or make much smaller) +# Overview + +This library enables the parallelization through partitioning of the consumption of messages from a stream while ensuring a strict order of not just delivery but also successful consumption of the messages using all or parts of the message's subject as a partitioning key. + +In JetStream terms, strictly ordered consumption is achieved when you set the consumer's 'max acks pending' value to 1. However, setting this on a JetStream consumer has the very unfortunate side effect of being very low throughput (limited by the network latency and processing speed) and not being horizontally scalable: only one message is being delivered and processed synchronously at a time from that JetStream consumer, no matter how many instances of the consuming application are deployed. + +The library allows the creation of 'consumer groups' on Stream, where each 'member' of the consumer group can consume from the group in parallel (with max acks pending 1 if needed), with the guarantee that in no way more than one message for a particular key can be consumed at the same time. Client applications wanting to consume messages from the group simply do so using a 'member name' and providing a callback. Even if more than one instance of a member is deployed, only one of those instances will be delivered messages at a time. + +The library takes care of the partitioning and the mapping of the partitions between the members of the group, the idea being that it is mostly transparent to the consuming application's developers who only need to join a consumer group, providing a member name and a callback to process and acknowledge the message when successfully processed. + +NATS Partitioned consumer groups come in two flavors: *elastic* and *static*. + +***Static*** partitioned consumer groups assume that the stream already has a partition number present as the first token of the message's subjects (something that can be done automatically when messages are stored into to the stream by setting a subject transform for the stream). You can only create and delete static consumer groups. Any change to the consumer group's config in the KV bucket will cause all the member instances for all members of the group to stop consuming. + +***Elastic*** partitioned consumer groups on the other hand are implemented differently: the stream doesn't need to already contain a partition number subject token and you can administratively add and drop members from the consumer group's config whenever you want without having to delete and re-create the consumer (like you have to with static consumer groups). + +***In both cases*** +In both cases you must specify when creating the consumer group the maximum number of members for the group (which is actually the number of partitions used when partitioning the messages), plus a list of "members" (named instances of the consuming application). The library takes care of distributing the members over the list of partitions using either a 'balanced' distribution (the partitions are evenly distributed between the members) or 'mappings' (where you assign administratively the mappings of partitions to the members). The membership list or mappings must be specified once at consumer group creation time for static consumer groups, but can be changed at any time for elastic consumer groups. + +Each consumer groups has a configuration which is stored in a KV bucket (named `static-consumer-groups` or `elastic-consumer-groups`). + +## Static + +Static consumer groups operate on a stream where the partition number has already been inserted in the subject as the first token of the messages. In this mode of operation, the library creates JetStream consumers (one per member of the group) directly on the stream. This is not elastic: you create the consumer with a list of members once, and you can not adjust that membership list or mapping for the life of the consumer group (if you want to change the mapping, up to you to delete and re-create the static partitioned consumer group, and to figure out which sequence number you may want this new static partitioned consumer group to start from). + +## Elastic + +Elastic consumer groups operate on any stream, the messages in the stream do not have the partition number present in their subjects. The membership list (or mapping) for the consumer can be adjusted administratively at any time and up to the max number of members defined initially. The consumer group library in this case creates a new work-queue stream that sources from the stream, inserting the partition number subject token on the way. The consumer group library takes care of creating this sourced stream and managing all the consumers on this stream according to the current membership, the developer only needs to provide a stream name, consumer group name and a member name and callback and make sure to ack the messages. You can specify (at creation time) a maximum size (in number of messages or bytes) for this working queue stream, but be aware that once this stream has reached its limit, it will pause the sourcing for at least 1 second (expecting messages to be consumed from the consumer group, thereby making room for more messages to be sourced) so you will want to set this value to more than 1 second's worth of message consumption by the clients of the consumer group or this could result in small delays in the consumption of messages from the consumer group. + +## High availability + +You can deploy and run multiple instances of the consuming application using the same member name, in that case only one of the running instances of the member will be 'pinned' and have messages delivered to it (thereby the other instances are effectively in hot standby). There are functions (`ElasticMemberStepDown()` and `StaticMemberStepDown`) to force a change of the currently pinned member instance. + +### The importance of AckWait for reactivity to faults + +The timers related to how quickly consumer group member instances react to faults (for example the currently pinned instance getting killed or suspended for an extended period of time) are related and derived from the AckWait value passed when joining the consumer group to consume messages, due to the limit of nats.go current implementation of `Consume`, if the AckWait value passed is less than 2 seconds _and_ the consumer group is caught up with the head of the stream, then you may see some (slow and harmless) flapping of the active instance for the members in the consumer group. If the AckWait value passed is 0 then the default AckWait value of 5 seconds is used. Note that in the case of static consumer groups without acknowledgements, you can adjust the Pinned TTL value by specifying an AckWait value in the ConsumerConfig you pass to static consume. + +## Using Partitioned Consumer Groups + +For the client application programmer, there is one basic functionality exposed by both static and elastic partitioned consumer groups: join and consume messages (when selected) from a named consumer group on a stream by specifying a _member name_, a regular JetStream consumer config, and a _callback_. The library takes care of stripping the partition number token from the subject such that you can use any existing callback code you may already have as is. + +There are also administrative functions to create and delete consumer groups, plus, in the case of elastic consumer groups only, the ability to add/drop members or to change the custom member to partition mappings on an existing elastic consumer group. + +## CLI + +Included is a small command line interface tool, named `cg` and located in the `cli` directory, that allows you to manage consumer groups, as well as test or demonstrate the functionality. + +This `cg` CLI tool can be used by passing it commands and arguments directly, or with an interactive prompt using the `prompt` command (e.g. `cg static prompt`). + +## Demo walkthrough + +### Static + +Create a stream "foo" that automatically partitions over 10 partitions using `static_stream_setup.sh`, then generate some traffic (a new message every 10ms) for that stream using `generate_traffic.sh`. + +Create a static consumer group named "cg" on the stream in question, with two members defined called "m1" and "m2": `java -jar cg.jar static create balanced foo cg 10 '>' m1 m2` + +Start consuming messages with a simulated processing time of 20ms from an instance of member "m1": `java -jar cg.jar static consume foo cg m1 --sleep 25ms`. Run in another window cg again to consume as member m2 a second, run multiple instances of members m1 and m2, kill the active one (the one receiving messages) and watch as one of the other instances takes over. + +### Elastic + +Create a stream 'foo' that captures messages on the subjects `foo.*`, then generate some traffic (a new message every 10ms) for that stream using `generate_traffic.sh`. + +Create an elastic consumer group named "cg", partitioning over 10 partitions using the second token (first `*` wildcard in the filter "foo.*") in the subject as the partitioning key: `java -jar cg.jar elastic create foo cg 10`. + +At this point the elastic consumer group is created, but no members have been added to it yet. But you can start instances of your consuming members already (e.g. `java -jar cg.jar elastic consume foo cg m1` for an instance of a member "m1"), for example start instances of members "m1", "m2" and "m3". At this point none of those members are receiving messages. + +Add "m1" and "m2" to the membership: `java -jar cg.jar elastic add foo cg m1 m2`, see how they start receiving messages. Then drop "m1" from the membership `java -jar cg.jar elastic drop foo cg m1`, add it again, and each time watch as the consumer starts and stops receiving messages, run another consumer "m3" and add/drop it from the membership, etc... + +As soon as the elastic consumer group is created, you can start instances of consuming clients (e.g. `java -jar cg.jar elastic consume foo cg m1`), and they will start to consume messages as soon as (and as long as) they are in the group's membership. + +### Example + +To start consuming from a static consumer group, you call `StaticConsumerGroup.consume`. To start consuming from an elastic consumer group you call `ElasticConsumerGroup.consume`. These calls will return a `ConsumerGroupConsumeContext`. Assuming no exception is thrown,this will create a few threads to handle handles consumption and monitoring for changes in the consumer group's config. + +e.g. for static +```java +ConsumerGroupConsumeContext ctx = StaticConsumerGroup.consume(nc, streamName, consumerGroupName, memberName, messageHandler, consumerConfig); +``` +The arguments are: +- `nc` is a NATS connection object. +- `streamName` is the name of the Stream on which the consumer group has been created. +- `consumerGroupName` is the name of the consumer group that has been created on the stream. +- `memberName` is the name of the member you want to join the consumer group as. +- `messageHandler` is a callback function that gets invoked and passed the messages for consumption. Note that if you are using an elastic consumer group you _must_ explicitly acknowledge (positively or negatively) the message in your callback. +- `config` is a regular JetStream consumer config to use by the library as a template when actually creating the JetStream consumers. For elastic consumers the acknowledgement policy must be explicit. For static consumer groups, it doesn't have to, but if you want to do strictly one at a time processing, you will need to use explicit acks in order for max acks pending 1 to apply. Note that this configuration being used as template means that some of the values will be overwritten and can be left empty (e.g. names and durable names, filters, priority groups) or will be overwritten. Note that there is a relationship that must be maintained between the ack wait time, the consumer fetch time out, and the pinned TTL values to avoid 'flapping' of the pinned client so those timeout and TTL values are computed from the value of AckWait passed in the ConsumerConfig, and as such the failover time between instances of the same member are affected by the AckWait value. + +- The `ConsumerGroupConsumeContext` returned lets you stop the consumption and get a future that completes when the consumer stops (e.g. when explicitly stopped, or when the consumer group gets deleted) or an error occurs. + +You can look at the `cg` CLI tool's source code for examples of how to create and consume for both static and elastic consumer groups. +# Requirements + +Partitioned consumer groups require NATS server version 2.11 or above. diff --git a/pcgroups/cli/pom.xml b/pcgroups/cli/pom.xml new file mode 100644 index 0000000..f31a38f --- /dev/null +++ b/pcgroups/cli/pom.xml @@ -0,0 +1,132 @@ + + + + 4.0.0 + + io.synadia + cg-cli + 0.1.0 + jar + + Partitioned Consumer Groups CLI + CLI tool for managing NATS JetStream Partitioned Consumer Groups + + + 11 + 11 + UTF-8 + 4.7.5 + 2.25.1 + 2.11.0 + + + + + + io.synadia + partitioned-consumer-groups + 0.1.0 + + + + + io.nats + jnats + ${jnats.version} + + + + + com.google.code.gson + gson + ${gson.version} + + + + + info.picocli + picocli + ${picocli.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 11 + 11 + + + info.picocli + picocli-codegen + ${picocli.version} + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.1 + + + package + + shade + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + io.synadia.pcg.cli.CgCommand + + + false + cg + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + io.synadia.pcg.cli.CgCommand + + + + + + + diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CgCommand.java b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CgCommand.java new file mode 100644 index 0000000..bea105b --- /dev/null +++ b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CgCommand.java @@ -0,0 +1,59 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg.cli; + +import picocli.CommandLine; +import picocli.CommandLine.*; + +import java.util.concurrent.Callable; + +/** + * Main CLI entry point for the Partitioned Consumer Group CLI tool. + * Run with: java -jar cg.jar [command] [options] + */ +@Command(name = "cg", + version = "0.1.0", + description = "Partitioned Consumer Group CLI tool", + mixinStandardHelpOptions = true, + subcommands = { + StaticCommands.class, + ElasticCommands.class + }) +public class CgCommand implements Callable { + + @Option(names = "--context", description = "NATS CLI context to use") + String context; + + @Override + public Integer call() { + System.out.println("Partitioned Consumer Group CLI tool v0.1.0"); + System.out.println(); + System.out.println("Usage: cg [options]"); + System.out.println(); + System.out.println("Commands:"); + System.out.println(" static Static consumer groups mode"); + System.out.println(" elastic Elastic consumer groups mode"); + System.out.println(); + System.out.println("Use 'cg --help' for more information about a command."); + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new CgCommand()) + .setAbbreviatedOptionsAllowed(true) + .setAbbreviatedSubcommandsAllowed(true) + .execute(args); + System.exit(exitCode); + } +} diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CliUtils.java b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CliUtils.java new file mode 100644 index 0000000..bd355de --- /dev/null +++ b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CliUtils.java @@ -0,0 +1,266 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg.cli; + +import io.nats.client.Connection; +import io.nats.client.JetStream; +import io.nats.client.JetStreamOptions; +import io.nats.client.Nats; +import io.nats.client.Options; +import io.synadia.pcg.MemberMapping; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +/** + * CLI helper utilities. + */ +public class CliUtils { + + private CliUtils() { + // Utility class + } + + /** + * Connects to NATS using the specified context or defaults. + */ + public static Connection connect(String contextName) throws IOException, InterruptedException { + Options.Builder builder = Options.builder(); + + if (contextName != null && !contextName.isEmpty()) { + // Try to load context from NATS CLI context file + NatsContext ctx = loadNatsContext(contextName); + if (ctx != null) { + if (ctx.url != null && !ctx.url.isEmpty()) { + builder.server(ctx.url); + } + if (ctx.user != null && !ctx.user.isEmpty() && ctx.password != null) { + builder.userInfo(ctx.user, ctx.password); + } + if (ctx.creds != null && !ctx.creds.isEmpty()) { + builder.credentialPath(ctx.creds); + } + if (ctx.nkey != null && !ctx.nkey.isEmpty()) { + builder.authHandler(Nats.staticCredentials(null, ctx.nkey.toCharArray())); + } + if (ctx.token != null && !ctx.token.isEmpty()) { + builder.token(ctx.token.toCharArray()); + } + } + } else { + // Default connection + String natsUrl = System.getenv("NATS_URL"); + if (natsUrl == null || natsUrl.isEmpty()) { + natsUrl = Options.DEFAULT_URL; + } + builder.server(natsUrl); + } + + return Nats.connect(builder.build()); + } + + /** + * Creates a JetStream context with optional domain. + */ + public static JetStream getJetStream(Connection nc, String contextName) throws IOException { + JetStreamOptions.Builder jsoBuilder = JetStreamOptions.builder(); + + if (contextName != null && !contextName.isEmpty()) { + NatsContext ctx = loadNatsContext(contextName); + if (ctx != null && ctx.jsDomain != null && !ctx.jsDomain.isEmpty()) { + jsoBuilder.domain(ctx.jsDomain); + } + } + + return nc.jetStream(jsoBuilder.build()); + } + + /** + * Parses member mapping arguments in the format "member:partition1,partition2,...". + */ + public static List parseMemberMappings(List mappingArgs) throws IllegalArgumentException { + List mappings = new ArrayList<>(); + + if (mappingArgs == null || mappingArgs.isEmpty()) { + return mappings; + } + + for (String mapping : mappingArgs) { + int colonIndex = mapping.indexOf(':'); + if (colonIndex < 0) { + throw new IllegalArgumentException("can't parse member mapping '" + mapping + "': missing ':'"); + } + + String memberName = mapping.substring(0, colonIndex); + String partitionsInput = mapping.substring(colonIndex + 1); + String[] partitionArgs = partitionsInput.split(","); + + int[] partitions = new int[partitionArgs.length]; + for (int i = 0; i < partitionArgs.length; i++) { + try { + partitions[i] = Integer.parseInt(partitionArgs[i].trim()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("can't parse partition '" + partitionArgs[i] + "': not a valid integer"); + } + } + + mappings.add(new MemberMapping(memberName, partitions)); + } + + return mappings; + } + + /** + * Parses partitioning wildcard indexes from a list of strings. + */ + public static int[] parsePartitioningWildcards(List wildcardArgs) throws IllegalArgumentException { + if (wildcardArgs == null || wildcardArgs.isEmpty()) { + return new int[0]; + } + + int[] wildcards = new int[wildcardArgs.size()]; + for (int i = 0; i < wildcardArgs.size(); i++) { + try { + wildcards[i] = Integer.parseInt(wildcardArgs.get(i).trim()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("can't parse wildcard index '" + wildcardArgs.get(i) + "': not a valid integer"); + } + } + + return wildcards; + } + + /** + * Simple NATS context structure. + */ + static class NatsContext { + String url; + String user; + String password; + String creds; + String nkey; + String token; + String jsDomain; + } + + /** + * Loads a NATS CLI context from the standard location. + */ + private static NatsContext loadNatsContext(String contextName) { + // Try to load from ~/.config/nats/context/.json + String configDir = System.getenv("XDG_CONFIG_HOME"); + if (configDir == null || configDir.isEmpty()) { + configDir = System.getProperty("user.home") + "/.config"; + } + + Path contextFile = Paths.get(configDir, "nats", "context", contextName + ".json"); + if (!Files.exists(contextFile)) { + return null; + } + + try { + String content = Files.readString(contextFile); + return parseContextJson(content); + } catch (IOException e) { + return null; + } + } + + /** + * Parses a simple JSON context file. + */ + private static NatsContext parseContextJson(String json) { + NatsContext ctx = new NatsContext(); + + // Simple JSON parsing without external dependencies + ctx.url = extractJsonValue(json, "url"); + ctx.user = extractJsonValue(json, "user"); + ctx.password = extractJsonValue(json, "password"); + ctx.creds = extractJsonValue(json, "creds"); + ctx.nkey = extractJsonValue(json, "nkey"); + ctx.token = extractJsonValue(json, "token"); + ctx.jsDomain = extractJsonValue(json, "jetstream_domain"); + + return ctx; + } + + private static String extractJsonValue(String json, String key) { + String pattern = "\"" + key + "\""; + int keyIndex = json.indexOf(pattern); + if (keyIndex < 0) { + return null; + } + + int colonIndex = json.indexOf(':', keyIndex + pattern.length()); + if (colonIndex < 0) { + return null; + } + + int valueStart = colonIndex + 1; + while (valueStart < json.length() && Character.isWhitespace(json.charAt(valueStart))) { + valueStart++; + } + + if (valueStart >= json.length()) { + return null; + } + + if (json.charAt(valueStart) == '"') { + int valueEnd = json.indexOf('"', valueStart + 1); + if (valueEnd > valueStart) { + return json.substring(valueStart + 1, valueEnd); + } + } + + return null; + } + + /** + * Formats a Duration as a human-readable string (e.g., "20ms", "5s", "1m30s"). + */ + public static String formatDuration(java.time.Duration duration) { + long totalNanos = duration.toNanos(); + if (totalNanos == 0) return "0s"; + + long hours = duration.toHours(); + long minutes = duration.toMinutesPart(); + long seconds = duration.toSecondsPart(); + long millis = duration.toMillisPart(); + + StringBuilder sb = new StringBuilder(); + if (hours > 0) sb.append(hours).append("h"); + if (minutes > 0) sb.append(minutes).append("m"); + if (seconds > 0) sb.append(seconds).append("s"); + if (millis > 0 && hours == 0 && minutes == 0 && seconds == 0) sb.append(millis).append("ms"); + + return sb.length() > 0 ? sb.toString() : "0s"; + } + + /** + * Prompts for user confirmation. + */ + public static boolean confirm(String message) { + System.out.print(message + " (y/n): "); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + String response = reader.readLine(); + return response != null && response.trim().equalsIgnoreCase("y"); + } catch (IOException e) { + return false; + } + } +} diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java new file mode 100644 index 0000000..30fa70d --- /dev/null +++ b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java @@ -0,0 +1,110 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg.cli; + +import picocli.CommandLine.ITypeConverter; + +import java.time.Duration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Converts human-readable duration strings to Duration objects. + * Supports formats like: 20ms, 5s, 1m, 2h, 1h30m, 500us, 100ns + */ +public class DurationConverter implements ITypeConverter { + + private static final Pattern DURATION_PATTERN = Pattern.compile( + "(?:(\\d+)h)?(?:(\\d+)m(?!s))?(?:(\\d+)s)?(?:(\\d+)ms)?(?:(\\d+)us)?(?:(\\d+)ns)?", + Pattern.CASE_INSENSITIVE + ); + + @Override + public Duration convert(String value) throws Exception { + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("Duration cannot be empty"); + } + + value = value.trim().toLowerCase(); + + // Try parsing as a simple number with unit suffix + Matcher matcher = DURATION_PATTERN.matcher(value); + if (matcher.matches()) { + long totalNanos = 0; + + String hours = matcher.group(1); + String minutes = matcher.group(2); + String seconds = matcher.group(3); + String millis = matcher.group(4); + String micros = matcher.group(5); + String nanos = matcher.group(6); + + if (hours != null) { + totalNanos += Long.parseLong(hours) * 3600_000_000_000L; + } + if (minutes != null) { + totalNanos += Long.parseLong(minutes) * 60_000_000_000L; + } + if (seconds != null) { + totalNanos += Long.parseLong(seconds) * 1_000_000_000L; + } + if (millis != null) { + totalNanos += Long.parseLong(millis) * 1_000_000L; + } + if (micros != null) { + totalNanos += Long.parseLong(micros) * 1_000L; + } + if (nanos != null) { + totalNanos += Long.parseLong(nanos); + } + + if (totalNanos > 0) { + return Duration.ofNanos(totalNanos); + } + } + + // Try single unit formats (e.g., "20ms", "5s") + Pattern singleUnit = Pattern.compile("(\\d+)(ns|us|ms|s|m|h)", Pattern.CASE_INSENSITIVE); + Matcher singleMatcher = singleUnit.matcher(value); + if (singleMatcher.matches()) { + long amount = Long.parseLong(singleMatcher.group(1)); + String unit = singleMatcher.group(2).toLowerCase(); + + switch (unit) { + case "ns": + return Duration.ofNanos(amount); + case "us": + return Duration.ofNanos(amount * 1000); + case "ms": + return Duration.ofMillis(amount); + case "s": + return Duration.ofSeconds(amount); + case "m": + return Duration.ofMinutes(amount); + case "h": + return Duration.ofHours(amount); + } + } + + throw new IllegalArgumentException( + "Invalid duration format: '" + value + "'. Use formats like: 20ms, 5s, 1m, 2h, 1h30m"); + } + + /** + * Static convenience method for parsing duration strings. + */ + public static Duration parseDuration(String value) throws Exception { + return new DurationConverter().convert(value); + } +} diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java new file mode 100644 index 0000000..5bec79c --- /dev/null +++ b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java @@ -0,0 +1,435 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg.cli; + +import io.nats.client.Connection; +import io.nats.client.api.AckPolicy; +import io.nats.client.api.ConsumerConfiguration; +import io.synadia.pcg.*; +import picocli.CommandLine.*; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; + +/** + * Elastic consumer group CLI commands. + */ +@Command(name = "elastic", description = "Elastic consumer groups mode", + subcommands = { + ElasticCommands.Ls.class, + ElasticCommands.Info.class, + ElasticCommands.Create.class, + ElasticCommands.Delete.class, + ElasticCommands.Add.class, + ElasticCommands.Drop.class, + ElasticCommands.CreateMapping.class, + ElasticCommands.DeleteMapping.class, + ElasticCommands.MemberInfo.class, + ElasticCommands.StepDown.class, + ElasticCommands.Consume.class, + ElasticCommands.Prompt.class + }) +public class ElasticCommands implements Callable { + + @ParentCommand + CgCommand parent; + + @Override + public Integer call() { + System.out.println("Use 'cg elastic --help' for available subcommands"); + return 0; + } + + @Command(name = "ls", aliases = {"list"}, description = "List elastic consumer groups for a stream") + static class Ls implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + List groups = ElasticConsumerGroup.list(nc, streamName); + System.out.println("elastic consumer groups: " + groups); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "info", description = "Get elastic consumer group info") + static class Info implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + ElasticConsumerGroupConfig config = ElasticConsumerGroup.getConfig(nc, streamName, consumerGroupName); + + System.out.printf("config: max members=%d, filter=%s, partitioning wildcards %s%n", + config.getMaxMembers(), config.getFilter(), Arrays.toString(config.getPartitioningWildcards())); + + if (!config.getMembers().isEmpty()) { + System.out.printf("members: %s%n", config.getMembers()); + } else if (!config.getMemberMappings().isEmpty()) { + System.out.printf("Member mappings: %s%n", config.getMemberMappings()); + } else { + System.out.println("no members or mappings defined"); + } + + List activeMembers = ElasticConsumerGroup.listActiveMembers(nc, streamName, consumerGroupName); + System.out.printf("currently active members: %s%n", activeMembers); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "create", description = "Create an elastic partitioned consumer group") + static class Create implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Max number of members") + int maxMembers; + + @Parameters(index = "3", description = "Filter") + String filter; + + @Parameters(index = "4..*", description = "Partitioning wildcard indexes") + List wildcardArgs; + + @Option(names = "--max-buffered-msgs", description = "Max number of buffered messages", defaultValue = "0") + long maxBufferedMsgs; + + @Option(names = "--max-buffered-bytes", description = "Max number of buffered bytes", defaultValue = "0") + long maxBufferedBytes; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + int[] wildcards = CliUtils.parsePartitioningWildcards(wildcardArgs); + ElasticConsumerGroup.create(nc, streamName, consumerGroupName, maxMembers, filter, wildcards, maxBufferedMsgs, maxBufferedBytes); + System.out.println("elastic partitioned consumer group created"); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "delete", aliases = {"rm"}, description = "Delete an elastic partitioned consumer group") + static class Delete implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Option(names = {"-f", "--force"}, description = "Force delete without confirmation") + boolean force; + + @Override + public Integer call() { + if (!force) { + if (!CliUtils.confirm("WARNING: this operation will cause all existing consumer members to terminate consuming. Are you sure?")) { + System.out.println("Operation canceled"); + return 1; + } + } + + try (Connection nc = CliUtils.connect(parent.parent.context)) { + ElasticConsumerGroup.delete(nc, streamName, consumerGroupName); + System.out.println("elastic consumer group deleted"); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "add", description = "Add members to an elastic consumer group") + static class Add implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2..*", description = "Member names") + List memberNames; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + List members = ElasticConsumerGroup.addMembers(nc, streamName, consumerGroupName, memberNames); + System.out.println("added members: " + members); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "drop", description = "Drop members from an elastic consumer group") + static class Drop implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2..*", description = "Member names") + List memberNames; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + List members = ElasticConsumerGroup.deleteMembers(nc, streamName, consumerGroupName, memberNames); + System.out.println("dropped members: " + members); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "create-mapping", aliases = {"cm", "createmapping"}, description = "Create member mappings for an elastic consumer group") + static class CreateMapping implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2..*", description = "Mappings in format member:partition1,partition2,...") + List mappingArgs; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + List memberMappings = CliUtils.parseMemberMappings(mappingArgs); + ElasticConsumerGroup.setMemberMappings(nc, streamName, consumerGroupName, memberMappings); + System.out.println("member mapping: " + memberMappings); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "delete-mapping", aliases = {"dm", "deletemapping"}, description = "Delete member mappings for an elastic consumer group") + static class DeleteMapping implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + ElasticConsumerGroup.deleteMemberMappings(nc, streamName, consumerGroupName); + System.out.println("member mappings deleted"); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "member-info", aliases = {"memberinfo", "minfo"}, description = "Get elastic consumer group member info") + static class MemberInfo implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Member name") + String memberName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + boolean[] status = ElasticConsumerGroup.isInMembershipAndActive(nc, streamName, consumerGroupName, memberName); + boolean inMembership = status[0]; + boolean isActive = status[1]; + + if (inMembership) { + if (isActive) { + System.out.printf("member %s is part of the consumer group membership and is active%n", memberName); + } else { + System.out.printf("member %s is part of the consumer group membership%n", memberName); + System.out.printf("***Warning*** member %s is part of the consumer group membership but has NO active instance%n", memberName); + } + } else { + System.out.printf("member %s is not currently part of the consumer group membership%n", memberName); + } + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "step-down", aliases = {"stepdown", "sd"}, description = "Initiate a step down for a member") + static class StepDown implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Member name") + String memberName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + ElasticConsumerGroup.memberStepDown(nc, streamName, consumerGroupName, memberName); + System.out.printf("member %s step down initiated%n", memberName); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "consume", aliases = {"join"}, description = "Join an elastic partitioned consumer group") + static class Consume implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Member name") + String memberName; + + @Option(names = "--sleep", description = "Sleep to simulate processing time (e.g., 20ms, 5s, 1m)", defaultValue = "20ms", converter = DurationConverter.class) + Duration processingDuration = Duration.ofMillis(20); + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + Duration processingTime = processingDuration; + + System.out.println("consuming..."); + ConsumerConfiguration consumerConfig = ConsumerConfiguration.builder() + .maxAckPending(1) + .ackWait(Duration.ofSeconds(2)) + .ackPolicy(AckPolicy.Explicit) + .build(); + ConsumerGroupConsumeContext ctx = ElasticConsumerGroup.consume(nc, streamName, consumerGroupName, memberName, + msg -> { + String pid = msg.getPinnedId(); + try { + long seqNumber = msg.getMessage().metaData().streamSequence(); + System.out.printf("[%s] subject=%s, seq=%d, pinnedID=%s. Processing for %s ... ", + memberName, msg.getSubject(), seqNumber, pid, CliUtils.formatDuration(processingTime)); + Thread.sleep(processingTime.toMillis()); + msg.ackSync(Duration.ofSeconds(5)); + System.out.println("acked"); + } catch (Exception e) { + System.out.println("message could not be acked! (it will be or may already have been re-delivered): " + e.getMessage()); + } + }, + consumerConfig); + + // Wait for completion + try { + ctx.done().get(); + System.out.println("instance returned with no error"); + } catch (Exception e) { + System.out.println("instance returned with an error: " + e.getMessage()); + } + + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "prompt", description = "Interactive prompt mode") + static class Prompt implements Callable { + @ParentCommand + private ElasticCommands parent; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + return new PromptHandler(false, parent.parent.context, nc).run(); + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } +} diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java new file mode 100644 index 0000000..9279742 --- /dev/null +++ b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java @@ -0,0 +1,688 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg.cli; + +import io.nats.client.Connection; +import io.nats.client.api.AckPolicy; +import io.nats.client.api.ConsumerConfiguration; +import io.synadia.pcg.*; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Shared interactive prompt handler for both static and elastic modes. + * Mirrors the Go prompt() function behavior. + */ +class PromptHandler { + + private boolean isStatic; + private final String context; + private final Connection nc; + private Duration processingDuration = Duration.ofMillis(20); + private boolean consuming = false; + private String currentStream; + private String currentGroup; + private String currentMember; + + PromptHandler(boolean isStatic, String context, Connection nc) { + this.isStatic = isStatic; + this.context = context; + this.nc = nc; + } + + int run() { + System.out.println("Interactive prompt mode - type 'help' for commands, 'exit' to quit"); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + while (true) { + try { + if (isStatic) { + System.out.print("[static]"); + } else { + System.out.print("[elastic]"); + } + if (consuming) { + System.out.printf("[%s/%s/%s]> ", currentStream, currentGroup, currentMember); + } else { + System.out.print("> "); + } + + String line = reader.readLine(); + if (line == null) break; + + line = line.trim(); + if (line.isEmpty()) continue; + + String command; + String argsString = null; + String[] args = null; + int spaceIndex = line.indexOf(' '); + if (spaceIndex >= 0) { + command = line.substring(0, spaceIndex); + argsString = line.substring(spaceIndex + 1).trim(); + args = argsString.split("\\s+"); + } else { + command = line; + args = new String[0]; + } + + switch (command) { + case "exit": + case "quit": + System.out.println("Exiting..."); + return 0; + case "help": + case "?": + printHelp(); + break; + case "static": + isStatic = true; + break; + case "elastic": + isStatic = false; + break; + case "processing-time": + handleProcessingTime(reader, args, argsString); + break; + case "list": + case "ls": + handleList(reader, args, argsString); + break; + case "info": + handleInfo(reader, args); + break; + case "create": + handleCreate(reader); + break; + case "delete": + case "rm": + handleDelete(reader, args); + break; + case "add": + handleAdd(reader, args); + break; + case "drop": + handleDrop(reader, args); + break; + case "createmapping": + case "create-mapping": + case "cm": + handleCreateMapping(reader, args); + break; + case "deletemapping": + case "delete-mapping": + case "dm": + handleDeleteMapping(reader, args); + break; + case "memberinfo": + case "member-info": + case "minfo": + handleMemberInfo(reader, args); + break; + case "stepdown": + case "step-down": + case "sd": + handleStepDown(reader, args); + break; + case "consume": + case "join": + handleConsume(reader, args); + break; + default: + System.out.println("Unknown command: " + command + ". Type 'help' for available commands."); + } + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + } + } + return 0; + } + + private void printHelp() { + System.out.println("Available commands:"); + System.out.println(" exit/quit - exit the program"); + System.out.println(" list/ls - list partitioned consumer groups"); + System.out.println(" info - get partitioned consumer group info"); + System.out.println(" create - create a partitioned consumer group"); + System.out.println(" delete/rm - delete a partitioned consumer group"); + System.out.println(" memberinfo/minfo - get partitioned consumer group member info"); + System.out.println(" add [...] - add a member to a partitioned consumer group"); + System.out.println(" drop [...] - remove a member from a partitioned consumer group"); + System.out.println(" deletemapping - delete all member mappings for a partitioned consumer group"); + System.out.println(" createmapping - create member mappings for a partitioned consumer group"); + System.out.println(" stepdown/sd - initiate a step down for a member"); + System.out.println(" consume/join - join a partitioned consumer group"); + System.out.println(" processing-time - set message processing time (e.g., 20ms, 5s, 1m)"); + System.out.println(" static - static consumer groups mode"); + System.out.println(" elastic - elastic consumer groups mode"); + } + + private void handleProcessingTime(BufferedReader reader, String[] args, String argsString) { + try { + String input; + if (args.length != 1) { + System.out.print("processing time: "); + input = reader.readLine(); + if (input == null) return; + input = input.trim(); + } else { + input = argsString; + } + processingDuration = DurationConverter.parseDuration(input); + System.out.printf("processing time set to %s%n", CliUtils.formatDuration(processingDuration)); + } catch (Exception e) { + System.out.printf("error: can't parse processing time: %s%n", e.getMessage()); + } + } + + private void handleList(BufferedReader reader, String[] args, String argsString) { + try { + if (args.length != 1) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + } else { + currentStream = argsString; + } + + List groups; + if (isStatic) { + groups = StaticConsumerGroup.list(nc, currentStream); + System.out.println("static consumer groups: " + groups); + } else { + groups = ElasticConsumerGroup.list(nc, currentStream); + System.out.println("elastic consumer groups: " + groups); + } + } catch (Exception e) { + System.out.printf("error: can't list partitioned consumer groups: %s%n", e.getMessage()); + } + } + + private void handleInfo(BufferedReader reader, String[] args) { + try { + if (args.length != 2) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + } else { + currentStream = args[0]; + currentGroup = args[1]; + } + + if (isStatic) { + StaticConsumerGroupConfig config = StaticConsumerGroup.getConfig(nc, currentStream, currentGroup); + System.out.printf("config: max members=%d, filter=%s%n", config.getMaxMembers(), config.getFilter()); + if (!config.getMembers().isEmpty()) { + System.out.printf("members: %s%n", config.getMembers()); + } else if (!config.getMemberMappings().isEmpty()) { + System.out.printf("Member mappings: %s%n", config.getMemberMappings()); + } else { + System.out.println("no members or mappings defined"); + } + List activeMembers = StaticConsumerGroup.listActiveMembers(nc, currentStream, currentGroup); + System.out.printf("currently active members: %s%n", activeMembers); + } else { + ElasticConsumerGroupConfig config = ElasticConsumerGroup.getConfig(nc, currentStream, currentGroup); + System.out.printf("config: max members=%d, filter=%s, partitioning wildcards %s%n", + config.getMaxMembers(), config.getFilter(), Arrays.toString(config.getPartitioningWildcards())); + if (!config.getMembers().isEmpty()) { + System.out.printf("members: %s%n", config.getMembers()); + } else if (!config.getMemberMappings().isEmpty()) { + System.out.printf("Member mappings: %s%n", config.getMemberMappings()); + } else { + System.out.println("no members or mappings defined"); + } + List activeMembers = ElasticConsumerGroup.listActiveMembers(nc, currentStream, currentGroup); + System.out.printf("currently active members: %s%n", activeMembers); + } + } catch (Exception e) { + if (isStatic) { + System.out.printf("can't get static partitioned consumer group config: %s%n", e.getMessage()); + } else { + System.out.printf("can't get elastic partitioned consumer group config: %s%n", e.getMessage()); + } + } + } + + private void handleCreate(BufferedReader reader) { + try { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + + System.out.print("max members: "); + input = reader.readLine(); + if (input == null) return; + int maxMembers = Integer.parseInt(input.trim()); + + System.out.print("filter: "); + input = reader.readLine(); + if (input == null) return; + String filter = input.trim(); + + if (isStatic) { + System.out.print("space separated set of members (hit return to set member mappings instead): "); + input = reader.readLine(); + if (input == null) return; + input = input.trim(); + + if (input.isEmpty()) { + System.out.println("enter the member mappings"); + List mappingArgs = inputMemberMappings(reader); + if (mappingArgs.isEmpty()) { + System.out.println("member mappings not defined, can't create the partitioned consumer group"); + return; + } + List memberMappings = CliUtils.parseMemberMappings(mappingArgs); + StaticConsumerGroup.create(nc, currentStream, currentGroup, maxMembers, filter, new ArrayList<>(), memberMappings); + System.out.println("static partitioned consumer group created"); + } else { + List memberNames = Arrays.asList(input.split("\\s+")); + StaticConsumerGroup.create(nc, currentStream, currentGroup, maxMembers, filter, memberNames, new ArrayList<>()); + System.out.println("static partitioned consumer group created"); + } + } else { + System.out.print("space separated partitioning wildcard indexes: "); + input = reader.readLine(); + if (input == null) return; + String[] pwciArgs = input.trim().split("\\s+"); + int[] wildcards = new int[pwciArgs.length]; + for (int i = 0; i < pwciArgs.length; i++) { + wildcards[i] = Integer.parseInt(pwciArgs[i]); + } + + System.out.print("max buffered messages (0 for no limit): "); + input = reader.readLine(); + if (input == null) return; + long maxBufferedMsgs = input.trim().isEmpty() ? 0 : Long.parseLong(input.trim()); + + System.out.print("max buffered bytes (0 for no limit): "); + input = reader.readLine(); + if (input == null) return; + long maxBufferedBytes = input.trim().isEmpty() ? 0 : Long.parseLong(input.trim()); + + ElasticConsumerGroup.create(nc, currentStream, currentGroup, maxMembers, filter, wildcards, maxBufferedMsgs, maxBufferedBytes); + System.out.println("elastic partitioned consumer group created"); + } + } catch (Exception e) { + System.out.printf("can't create partitioned consumer group: %s%n", e.getMessage()); + } + } + + private void handleDelete(BufferedReader reader, String[] args) { + try { + if (args.length != 2) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + } else { + currentStream = args[0]; + currentGroup = args[1]; + } + + if (isStatic) { + StaticConsumerGroup.delete(nc, currentStream, currentGroup); + System.out.println("static consumer group deleted"); + } else { + ElasticConsumerGroup.delete(nc, currentStream, currentGroup); + System.out.println("elastic consumer group deleted"); + } + } catch (Exception e) { + System.out.printf("can't delete partitioned consumer group: %s%n", e.getMessage()); + } + } + + private void handleAdd(BufferedReader reader, String[] args) { + try { + if (isStatic) { + System.out.println("can not add members to a static partitioned consumer groups, you must delete and recreate them"); + return; + } + + List memberNames; + if (args.length < 3) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + System.out.print("member name (or space separated list of names): "); + input = reader.readLine(); + if (input == null) return; + memberNames = Arrays.asList(input.trim().split("\\s+")); + } else { + currentStream = args[0]; + currentGroup = args[1]; + memberNames = Arrays.asList(Arrays.copyOfRange(args, 2, args.length)); + } + + List members = ElasticConsumerGroup.addMembers(nc, currentStream, currentGroup, memberNames); + System.out.println("added members: " + members); + } catch (Exception e) { + System.out.printf("can't add members: %s%n", e.getMessage()); + } + } + + private void handleDrop(BufferedReader reader, String[] args) { + try { + if (isStatic) { + System.out.println("can not drop members from a static partitioned consumer groups, you must delete and recreate them"); + return; + } + + List memberNames; + if (args.length < 3) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + System.out.print("member name (or space separated list of names): "); + input = reader.readLine(); + if (input == null) return; + memberNames = Arrays.asList(input.trim().split("\\s+")); + } else { + currentStream = args[0]; + currentGroup = args[1]; + memberNames = Arrays.asList(Arrays.copyOfRange(args, 2, args.length)); + } + + List members = ElasticConsumerGroup.deleteMembers(nc, currentStream, currentGroup, memberNames); + System.out.println("dropped members: " + members); + } catch (Exception e) { + System.out.printf("can't drop members: %s%n", e.getMessage()); + } + } + + private void handleCreateMapping(BufferedReader reader, String[] args) { + try { + if (args.length != 2) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + } else { + currentStream = args[0]; + currentGroup = args[1]; + } + + List mappingArgs = inputMemberMappings(reader); + List memberMappings = CliUtils.parseMemberMappings(mappingArgs); + + if (isStatic) { + // For static, we'd need to recreate - not directly supported + System.out.println("can not set member mappings on a static partitioned consumer group, you must delete and recreate it"); + } else { + ElasticConsumerGroup.setMemberMappings(nc, currentStream, currentGroup, memberMappings); + System.out.printf("member mappings set: %s%n", memberMappings); + } + } catch (Exception e) { + System.out.printf("can't set member mappings: %s%n", e.getMessage()); + } + } + + private void handleDeleteMapping(BufferedReader reader, String[] args) { + try { + if (args.length != 2) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + } else { + currentStream = args[0]; + currentGroup = args[1]; + } + + if (!CliUtils.confirm("WARNING: this operation will cause all existing consumer members to terminate consuming are you sure?")) { + return; + } + + if (isStatic) { + System.out.println("can not delete member mappings on a static partitioned consumer group, you must delete and recreate it"); + } else { + ElasticConsumerGroup.deleteMemberMappings(nc, currentStream, currentGroup); + System.out.println("member mappings deleted"); + } + } catch (Exception e) { + System.out.printf("can't delete member mappings: %s%n", e.getMessage()); + } + } + + private void handleMemberInfo(BufferedReader reader, String[] args) { + try { + if (args.length != 3) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + System.out.print("member name: "); + input = reader.readLine(); + if (input == null) return; + currentMember = input.trim(); + } else { + currentStream = args[0]; + currentGroup = args[1]; + currentMember = args[2]; + } + + if (isStatic) { + StaticConsumerGroupConfig config = StaticConsumerGroup.getConfig(nc, currentStream, currentGroup); + List activeMembers = StaticConsumerGroup.listActiveMembers(nc, currentStream, currentGroup); + + if (config.isInMembership(currentMember)) { + System.out.printf("member %s is part of the consumer group membership%n", currentMember); + if (activeMembers.contains(currentMember)) { + System.out.printf("member %s is active%n", currentMember); + } else { + System.out.printf("***Warning*** member %s is part of the consumer group membership but has NO active instance%n", currentMember); + } + } else { + System.out.printf("member %s is not part of the consumer group membership%n", currentMember); + } + } else { + boolean[] status = ElasticConsumerGroup.isInMembershipAndActive(nc, currentStream, currentGroup, currentMember); + boolean inMembership = status[0]; + boolean isActive = status[1]; + + if (inMembership) { + if (isActive) { + System.out.printf("member %s is part of the consumer group membership and is active%n", currentMember); + } else { + System.out.printf("member %s is part of the consumer group membership%n", currentMember); + System.out.printf("***Warning*** member %s is part of the consumer group membership but has NO active instance%n", currentMember); + } + } else { + System.out.printf("member %s is not currently part of the consumer group membership%n", currentMember); + } + } + } catch (Exception e) { + System.out.printf("can't get partitioned consumer group member info: %s%n", e.getMessage()); + } + } + + private void handleStepDown(BufferedReader reader, String[] args) { + try { + if (args.length != 3) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + System.out.print("member name: "); + input = reader.readLine(); + if (input == null) return; + currentMember = input.trim(); + } else { + currentStream = args[0]; + currentGroup = args[1]; + currentMember = args[2]; + } + + if (isStatic) { + StaticConsumerGroup.memberStepDown(nc, currentStream, currentGroup, currentMember); + } else { + ElasticConsumerGroup.memberStepDown(nc, currentStream, currentGroup, currentMember); + } + System.out.printf("member %s step down initiated%n", currentMember); + } catch (Exception e) { + System.out.printf("can't step down member: %s%n", e.getMessage()); + } + } + + private void handleConsume(BufferedReader reader, String[] args) { + try { + if (consuming) { + System.out.println("already consuming"); + return; + } + + if (args.length != 3) { + System.out.print("stream name: "); + String input = reader.readLine(); + if (input == null) return; + currentStream = input.trim(); + System.out.print("consumer group name: "); + input = reader.readLine(); + if (input == null) return; + currentGroup = input.trim(); + System.out.print("member name: "); + input = reader.readLine(); + if (input == null) return; + currentMember = input.trim(); + } else { + currentStream = args[0]; + currentGroup = args[1]; + currentMember = args[2].trim(); + } + + Duration processingTime = processingDuration; + String memberName = currentMember; + + System.out.println("consuming..."); + ConsumerConfiguration consumerConfig = ConsumerConfiguration.builder() + .maxAckPending(1) + .ackWait(Duration.ofSeconds(2)) + .ackPolicy(AckPolicy.Explicit) + .build(); + + ConsumerGroupConsumeContext ctx; + if (isStatic) { + ctx = StaticConsumerGroup.consume(nc, currentStream, currentGroup, memberName, + msg -> { + String pid = msg.getPinnedId(); + try { + long seqNumber = msg.getMessage().metaData().streamSequence(); + System.out.printf("[%s] subject=%s, seq=%d, pinnedID=%s. Processing for %s ... ", + memberName, msg.getSubject(), seqNumber, pid, CliUtils.formatDuration(processingTime)); + Thread.sleep(processingTime.toMillis()); + msg.ackSync(Duration.ofSeconds(5)); + System.out.println("acked"); + } catch (Exception e) { + System.out.println("message could not be acked! (it will be or may already have been re-delivered): " + e.getMessage()); + } + }, + consumerConfig); + } else { + ctx = ElasticConsumerGroup.consume(nc, currentStream, currentGroup, memberName, + msg -> { + String pid = msg.getPinnedId(); + try { + long seqNumber = msg.getMessage().metaData().streamSequence(); + System.out.printf("[%s] subject=%s, seq=%d, pinnedID=%s. Processing for %s ... ", + memberName, msg.getSubject(), seqNumber, pid, CliUtils.formatDuration(processingTime)); + Thread.sleep(processingTime.toMillis()); + msg.ackSync(Duration.ofSeconds(5)); + System.out.println("acked"); + } catch (Exception e) { + System.out.println("message could not be acked! (it will be or may already have been re-delivered): " + e.getMessage()); + } + }, + consumerConfig); + } + + consuming = true; + + // Wait for completion + try { + ctx.done().get(); + System.out.println("instance returned with no error"); + } catch (Exception e) { + System.out.println("instance returned with an error: " + e.getMessage()); + } + consuming = false; + } catch (Exception e) { + System.out.printf("can't join the partitioned consumer group: %s%n", e.getMessage()); + consuming = false; + } + } + + private List inputMemberMappings(BufferedReader reader) { + List mappings = new ArrayList<>(); + System.out.println("enter member mappings in format member:partition1,partition2,... (empty line to finish)"); + try { + while (true) { + System.out.print("mapping (or empty to finish): "); + String input = reader.readLine(); + if (input == null || input.trim().isEmpty()) break; + mappings.add(input.trim()); + } + } catch (Exception e) { + System.err.println("Error reading input: " + e.getMessage()); + } + return mappings; + } +} diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java new file mode 100644 index 0000000..5885ae9 --- /dev/null +++ b/pcgroups/cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java @@ -0,0 +1,363 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg.cli; + +import io.nats.client.Connection; +import io.nats.client.api.AckPolicy; +import io.nats.client.api.ConsumerConfiguration; +import io.synadia.pcg.*; +import picocli.CommandLine.*; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +/** + * Static consumer group CLI commands. + */ +@Command(name = "static", description = "Static consumer groups mode", + subcommands = { + StaticCommands.Ls.class, + StaticCommands.Info.class, + StaticCommands.Create.class, + StaticCommands.Delete.class, + StaticCommands.MemberInfo.class, + StaticCommands.StepDown.class, + StaticCommands.Consume.class, + StaticCommands.Prompt.class + }) +public class StaticCommands implements Callable { + + @ParentCommand + CgCommand parent; + + @Override + public Integer call() { + System.out.println("Use 'cg static --help' for available subcommands"); + return 0; + } + + @Command(name = "ls", aliases = {"list"}, description = "List static consumer groups for a stream") + static class Ls implements Callable { + @ParentCommand + private StaticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + List groups = StaticConsumerGroup.list(nc, streamName); + System.out.println("static consumer groups: " + groups); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "info", description = "Get static consumer group info") + static class Info implements Callable { + @ParentCommand + private StaticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + StaticConsumerGroupConfig config = StaticConsumerGroup.getConfig(nc, streamName, consumerGroupName); + + System.out.printf("config: max members=%d, filter=%s%n", config.getMaxMembers(), config.getFilter()); + + if (!config.getMembers().isEmpty()) { + System.out.printf("members: %s%n", config.getMembers()); + } else if (!config.getMemberMappings().isEmpty()) { + System.out.printf("Member mappings: %s%n", config.getMemberMappings()); + } else { + System.out.println("no members or mappings defined"); + } + + List activeMembers = StaticConsumerGroup.listActiveMembers(nc, streamName, consumerGroupName); + System.out.printf("currently active members: %s%n", activeMembers); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "create", description = "Create a static partitioned consumer group", + subcommands = {StaticCommands.CreateBalanced.class, StaticCommands.CreateMapped.class}) + static class Create implements Callable { + @ParentCommand + private StaticCommands parent; + + @Override + public Integer call() { + System.out.println("Use 'cg static create balanced' or 'cg static create mapped'"); + return 0; + } + } + + @Command(name = "balanced", description = "Create a static consumer group with balanced members") + static class CreateBalanced implements Callable { + @ParentCommand + private Create createParent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Max number of members") + int maxMembers; + + @Parameters(index = "3", description = "Filter") + String filter; + + @Parameters(index = "4..*", arity = "1..*", description = "Member names") + List memberNames; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(createParent.parent.parent.context)) { + StaticConsumerGroup.create(nc, streamName, consumerGroupName, maxMembers, filter, memberNames, new ArrayList<>()); + System.out.println("static partitioned consumer group created"); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "mapped", description = "Create a static consumer group with member mappings") + static class CreateMapped implements Callable { + @ParentCommand + private Create createParent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Max number of members") + int maxMembers; + + @Parameters(index = "3", description = "Filter") + String filter; + + @Parameters(index = "4..*", description = "Mappings in format member:partition1,partition2,...") + List mappingArgs; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(createParent.parent.parent.context)) { + List memberMappings = CliUtils.parseMemberMappings(mappingArgs); + StaticConsumerGroup.create(nc, streamName, consumerGroupName, maxMembers, filter, new ArrayList<>(), memberMappings); + System.out.println("static partitioned consumer group created"); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "delete", aliases = {"rm"}, description = "Delete a static partitioned consumer group") + static class Delete implements Callable { + @ParentCommand + private StaticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Option(names = {"-f", "--force"}, description = "Force delete without confirmation") + boolean force; + + @Override + public Integer call() { + if (!force) { + if (!CliUtils.confirm("WARNING: this operation will cause all existing consumer members to terminate consuming. Are you sure?")) { + System.out.println("Operation canceled"); + return 1; + } + } + + try (Connection nc = CliUtils.connect(parent.parent.context)) { + StaticConsumerGroup.delete(nc, streamName, consumerGroupName); + System.out.println("static consumer group deleted"); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "member-info", aliases = {"memberinfo", "minfo"}, description = "Get static consumer group member info") + static class MemberInfo implements Callable { + @ParentCommand + private StaticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Member name") + String memberName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + StaticConsumerGroupConfig config = StaticConsumerGroup.getConfig(nc, streamName, consumerGroupName); + List activeMembers = StaticConsumerGroup.listActiveMembers(nc, streamName, consumerGroupName); + + if (config.isInMembership(memberName)) { + System.out.printf("member %s is part of the consumer group membership%n", memberName); + if (activeMembers.contains(memberName)) { + System.out.printf("member %s is active%n", memberName); + } else { + System.out.printf("***Warning*** member %s is part of the consumer group membership but has NO active instance%n", memberName); + } + } else { + System.out.printf("member %s is not part of the consumer group membership%n", memberName); + } + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "step-down", aliases = {"stepdown", "sd"}, description = "Initiate a step down for a member") + static class StepDown implements Callable { + @ParentCommand + private StaticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Member name") + String memberName; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + StaticConsumerGroup.memberStepDown(nc, streamName, consumerGroupName, memberName); + System.out.printf("member %s step down initiated%n", memberName); + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "consume", aliases = {"join"}, description = "Join a static partitioned consumer group") + static class Consume implements Callable { + @ParentCommand + private StaticCommands parent; + + @Parameters(index = "0", description = "Stream name") + String streamName; + + @Parameters(index = "1", description = "Consumer group name") + String consumerGroupName; + + @Parameters(index = "2", description = "Member name") + String memberName; + + @Option(names = "--sleep", description = "Sleep to simulate processing time (e.g., 20ms, 5s, 1m)", defaultValue = "20ms", converter = DurationConverter.class) + Duration processingDuration = Duration.ofMillis(20); + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + Duration processingTime = processingDuration; + + System.out.println("consuming..."); + ConsumerConfiguration consumerConfig = ConsumerConfiguration.builder() + .maxAckPending(1) + .ackWait(Duration.ofSeconds(2)) + .ackPolicy(AckPolicy.Explicit) + .build(); + ConsumerGroupConsumeContext ctx = StaticConsumerGroup.consume(nc, streamName, consumerGroupName, memberName, + msg -> { + String pid = msg.getPinnedId(); + try { + long seqNumber = msg.getMessage().metaData().streamSequence(); + System.out.printf("[%s] subject=%s, seq=%d, pinnedID=%s. Processing for %s ... ", + memberName, msg.getSubject(), seqNumber, pid, CliUtils.formatDuration(processingTime)); + Thread.sleep(processingTime.toMillis()); + msg.ackSync(Duration.ofSeconds(5)); + System.out.println("acked"); + } catch (Exception e) { + System.out.println("message could not be acked! (it will be or may already have been re-delivered): " + e.getMessage()); + } + }, + consumerConfig); + + // Wait for completion + try { + ctx.done().get(); + System.out.println("instance returned with no error"); + } catch (Exception e) { + System.out.println("instance returned with an error: " + e.getMessage()); + } + + return 0; + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } + + @Command(name = "prompt", description = "Interactive prompt mode") + static class Prompt implements Callable { + @ParentCommand + private StaticCommands parent; + + @Override + public Integer call() { + try (Connection nc = CliUtils.connect(parent.parent.context)) { + return new PromptHandler(true, parent.parent.context, nc).run(); + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + return 1; + } + } + } +} diff --git a/pcgroups/pom.xml b/pcgroups/pom.xml new file mode 100644 index 0000000..cba0810 --- /dev/null +++ b/pcgroups/pom.xml @@ -0,0 +1,91 @@ + + + + 4.0.0 + + io.synadia + partitioned-consumer-groups + 0.1.0 + jar + + Partitioned Consumer Groups + NATS JetStream Partitioned Consumer Groups Library for Java + + + 11 + 11 + UTF-8 + 2.25.1 + 2.11.0 + 5.10.2 + + + + + + io.nats + jnats + ${jnats.version} + + + + + com.google.code.gson + gson + ${gson.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + io.nats + jnats-server-runner + 3.1.0 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 11 + 11 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + diff --git a/pcgroups/scripts/elastic_stream_setup.sh b/pcgroups/scripts/elastic_stream_setup.sh new file mode 100755 index 0000000..618a003 --- /dev/null +++ b/pcgroups/scripts/elastic_stream_setup.sh @@ -0,0 +1,2 @@ +#!/bin/bash +nats stream add foo --subjects="foo.*" --defaults \ No newline at end of file diff --git a/pcgroups/scripts/generate_traffic.sh b/pcgroups/scripts/generate_traffic.sh new file mode 100755 index 0000000..fd2a0c8 --- /dev/null +++ b/pcgroups/scripts/generate_traffic.sh @@ -0,0 +1,2 @@ +#!/bin/bash +nats bench js pub async foo --multisubject --sleep 10ms --multisubjectmax 100 --stream foo diff --git a/pcgroups/scripts/static_stream_setup.sh b/pcgroups/scripts/static_stream_setup.sh new file mode 100755 index 0000000..bf558c5 --- /dev/null +++ b/pcgroups/scripts/static_stream_setup.sh @@ -0,0 +1,2 @@ +#!/bin/bash +nats stream add foo --subjects="foo.*" --transform-source="foo.*" --transform-destination="{{partition(10,1)}}.foo.{{wildcard(1)}}" --defaults \ No newline at end of file diff --git a/pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupConsumeContext.java b/pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupConsumeContext.java new file mode 100644 index 0000000..05339db --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupConsumeContext.java @@ -0,0 +1,33 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import java.util.concurrent.CompletableFuture; + +/** + * Interface for controlling the consume lifecycle of a consumer group member. + */ +public interface ConsumerGroupConsumeContext { + + /** + * Stops consuming messages. + */ + void stop(); + + /** + * Returns a future that completes when the consumer stops or an error occurs. + * The future completes with an exception if an error occurred. + */ + CompletableFuture done(); +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupMsg.java b/pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupMsg.java new file mode 100644 index 0000000..e3501a6 --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/ConsumerGroupMsg.java @@ -0,0 +1,140 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import io.nats.client.Message; +import io.nats.client.impl.Headers; + +import java.io.IOException; +import java.time.Duration; + +/** + * Wrapper for JetStream messages that strips the partition number from the subject. + */ +public class ConsumerGroupMsg { + + private final Message msg; + + public ConsumerGroupMsg(Message msg) { + this.msg = msg; + } + + /** + * Returns the message body data. + */ + public byte[] getData() { + return msg.getData(); + } + + /** + * Returns the message headers. + */ + public Headers getHeaders() { + return msg.getHeaders(); + } + + /** + * Returns the subject with the partition number prefix stripped. + * The original subject format is "{partitionNumber}.{originalSubject}". + */ + public String getSubject() { + String subject = msg.getSubject(); + if (subject == null) { + return null; + } + int dotIndex = subject.indexOf('.'); + if (dotIndex >= 0 && dotIndex < subject.length() - 1) { + return subject.substring(dotIndex + 1); + } + return subject; + } + + /** + * Returns the original subject including partition prefix. + */ + public String getRawSubject() { + return msg.getSubject(); + } + + /** + * Returns the reply subject for the message. + */ + public String getReplyTo() { + return msg.getReplyTo(); + } + + /** + * Returns the underlying message for metadata access. + */ + public Message getMessage() { + return msg; + } + + /** + * Acknowledges the message. + * This tells the server that the message was successfully processed. + */ + public void ack() throws IOException, InterruptedException { + msg.ack(); + } + + /** + * Acknowledges the message and waits for acknowledgment from server. + */ + public void ackSync(Duration timeout) throws IOException, InterruptedException, java.util.concurrent.TimeoutException { + msg.ackSync(timeout); + } + + /** + * Negatively acknowledges the message. + * This tells the server to redeliver the message. + */ + public void nak() throws IOException, InterruptedException { + msg.nak(); + } + + /** + * Negatively acknowledges the message with a delay. + * This tells the server to redeliver the message after the specified delay. + */ + public void nakWithDelay(Duration delay) throws IOException, InterruptedException { + msg.nakWithDelay(delay); + } + + /** + * Tells the server that this message is being worked on. + * Resets the redelivery timer on the server. + */ + public void inProgress() throws IOException, InterruptedException { + msg.inProgress(); + } + + /** + * Tells the server to not redeliver this message, regardless of MaxDeliver setting. + */ + public void term() throws IOException, InterruptedException { + msg.term(); + } + + /** + * Returns the pinned ID from message headers if present. + */ + public String getPinnedId() { + Headers headers = msg.getHeaders(); + if (headers != null) { + return headers.getFirst("Nats-Pin-Id"); + } + return null; + } +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java new file mode 100644 index 0000000..b26a668 --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java @@ -0,0 +1,968 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import io.nats.client.*; +import io.nats.client.api.*; +import io.nats.client.impl.Headers; +import io.synadia.pcg.exceptions.ConsumerGroupException; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import static io.synadia.pcg.PartitionUtils.*; + +/** + * Elastic consumer group implementation. + * Provides dynamic partition assignment for consumer groups with a work queue stream. + */ +public class ElasticConsumerGroup { + + private static final Logger LOGGER = Logger.getLogger(ElasticConsumerGroup.class.getName()); + private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + + private ElasticConsumerGroup() { + // Utility class + } + + /** + * Creates an elastic consumer group. + * + * @param nc NATS connection + * @param streamName Name of the source stream + * @param consumerGroupName Name of the consumer group + * @param maxMembers Maximum number of members (partitions) + * @param filter Subject filter with wildcards + * @param partitioningWildcards Indexes of wildcards to use for partitioning + * @param maxBufferedMsgs Max messages in work queue (0 for unlimited) + * @param maxBufferedBytes Max bytes in work queue (0 for unlimited) + * @return The created configuration + */ + public static ElasticConsumerGroupConfig create(Connection nc, String streamName, String consumerGroupName, + int maxMembers, String filter, int[] partitioningWildcards, + long maxBufferedMsgs, long maxBufferedBytes) + throws ConsumerGroupException, IOException, JetStreamApiException, InterruptedException { + + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + maxMembers, filter, partitioningWildcards, maxBufferedMsgs, maxBufferedBytes, + new ArrayList<>(), new ArrayList<>()); + config.validate(); + + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Get stream info to determine replicas and storage type + StreamInfo streamInfo = jsm.getStreamInfo(streamName); + int replicas = streamInfo.getConfiguration().getReplicas(); + StorageType storage = streamInfo.getConfiguration().getStorageType(); + + // Get or create the KV bucket + KeyValueManagement kvm = nc.keyValueManagement(); + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + // Create the bucket if it doesn't exist + KeyValueConfiguration kvConfig = KeyValueConfiguration.builder() + .name(KV_ELASTIC_BUCKET_NAME) + .replicas(replicas) + .storageType(StorageType.File) + .build(); + kvm.create(kvConfig); + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } + + String key = composeKey(streamName, consumerGroupName); + + // Check if config already exists + KeyValueEntry entry = kv.get(key); + if (entry != null) { + String json = new String(entry.getValue(), StandardCharsets.UTF_8); + ElasticConsumerGroupConfig existingConfig = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + + // Verify the config matches + if (existingConfig.getMaxMembers() != maxMembers || + !Objects.equals(existingConfig.getFilter(), filter) || + existingConfig.getMaxBufferedMsgs() != maxBufferedMsgs || + existingConfig.getMaxBufferedBytes() != maxBufferedBytes || + !Arrays.equals(existingConfig.getPartitioningWildcards(), partitioningWildcards)) { + throw new ConsumerGroupException( + "the existing elastic consumer group config can not be updated to the requested one, " + + "please delete the existing elastic consumer group and create a new one"); + } + return existingConfig; + } + + // Create the config entry + String payload = GSON.toJson(config); + kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + + // Create the work queue stream with subject transform + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + String filterDest = getPartitioningTransformDest(config); + + StreamConfiguration.Builder scBuilder = StreamConfiguration.builder() + .name(workQueueStreamName) + .retentionPolicy(RetentionPolicy.WorkQueue) + .replicas(replicas) + .storageType(storage) + .discardPolicy(DiscardPolicy.New) + .allowDirect(true); + + if (maxBufferedMsgs > 0) { + scBuilder.maxMessages(maxBufferedMsgs); + } + if (maxBufferedBytes > 0) { + scBuilder.maxBytes(maxBufferedBytes); + } + + // Add source with subject transform + External external = null; // Local stream + scBuilder.addSource(Source.builder() + .sourceName(streamName) + .startSeq(0) + .subjectTransforms(SubjectTransform.builder() + .source(filter) + .destination(filterDest) + .build()) + .build()); + + try { + jsm.addStream(scBuilder.build()); + } catch (JetStreamApiException e) { + throw new ConsumerGroupException("can't create the elastic consumer group's stream: " + e.getMessage(), e); + } + + return config; + } + + /** + * Starts consuming messages from an elastic consumer group. + * + * @param nc NATS connection + * @param streamName Name of the source stream + * @param consumerGroupName Name of the consumer group + * @param memberName Name of this member + * @param handler Message handler callback + * @param consumerConfig Consumer configuration (null for defaults) + * @return A consume context for controlling the consumption lifecycle + */ + public static ConsumerGroupConsumeContext consume(Connection nc, String streamName, String consumerGroupName, + String memberName, Consumer handler, + ConsumerConfiguration consumerConfig) + throws ConsumerGroupException, IOException, JetStreamApiException, InterruptedException { + if (handler == null) { + throw new ConsumerGroupException("a message handler must be provided"); + } + + if (consumerConfig == null) { + consumerConfig = ConsumerConfiguration.builder() + .ackWait(DEFAULT_ACK_WAIT) + .ackPolicy(AckPolicy.Explicit) + .build(); + } + + if (consumerConfig.getAckPolicy() != null && consumerConfig.getAckPolicy() != AckPolicy.Explicit) { + throw new ConsumerGroupException("the ack policy when consuming from elastic consumer groups must be explicit"); + } + + if (consumerConfig.getAckWait() == null || consumerConfig.getAckWait().isZero() || consumerConfig.getAckWait().isNegative()) { + consumerConfig = ConsumerConfiguration.builder(consumerConfig).ackWait(DEFAULT_ACK_WAIT).build(); + } + + consumerConfig = ConsumerConfiguration.builder(consumerConfig) + .inactiveThreshold(Duration.ofMillis(consumerConfig.getAckWait().toMillis() * CONSUMER_IDLE_TIMEOUT_FACTOR)) + .build(); + + // Verify the work queue stream exists + JetStreamManagement jsm = nc.jetStreamManagement(); + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + try { + jsm.getStreamInfo(workQueueStreamName); + } catch (JetStreamApiException e) { + throw new ConsumerGroupException("the elastic consumer group's stream does not exist", e); + } + + // Get the KV bucket + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + // Get the config + ElasticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + return new ElasticConsumeContextImpl(nc, kv, streamName, consumerGroupName, memberName, config, handler, consumerConfig); + } + + /** + * Deletes an elastic consumer group. + */ + public static void delete(Connection nc, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Get the KV bucket and delete the config entry + try { + KeyValue kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + String key = composeKey(streamName, consumerGroupName); + kv.delete(key); + } catch (Exception e) { + // Ignore if bucket or key doesn't exist + } + + // Delete the work queue stream + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + try { + jsm.deleteStream(workQueueStreamName); + } catch (JetStreamApiException e) { + throw new ConsumerGroupException("could not delete the elastic consumer group's stream: " + e.getMessage(), e); + } + } + + /** + * Lists elastic consumer groups for a stream. + */ + public static List list(Connection nc, String streamName) + throws IOException, JetStreamApiException, InterruptedException { + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + return new ArrayList<>(); + } + + List keys = kv.keys(); + List consumerGroupNames = new ArrayList<>(); + + for (String key : keys) { + String[] parts = key.split("\\."); + if (parts.length >= 2 && parts[0].equals(streamName)) { + consumerGroupNames.add(parts[1]); + } + } + + return consumerGroupNames; + } + + /** + * Adds members to an elastic consumer group. + * + * @return The updated list of members + */ + public static List addMembers(Connection nc, String streamName, String consumerGroupName, + List memberNamesToAdd) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + if (streamName == null || streamName.isEmpty() || + consumerGroupName == null || consumerGroupName.isEmpty() || + memberNamesToAdd == null || memberNamesToAdd.isEmpty()) { + throw new ConsumerGroupException("invalid stream name or elastic consumer group name or no member names"); + } + + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + ElasticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + if (!config.getMemberMappings().isEmpty()) { + throw new ConsumerGroupException("can't add members to an elastic consumer group that uses member mappings"); + } + + Set existingMembers = new LinkedHashSet<>(config.getMembers()); + for (String memberName : memberNamesToAdd) { + if (memberName != null && !memberName.isEmpty()) { + existingMembers.add(memberName); + } + } + + List newMembers = new ArrayList<>(existingMembers); + config.setMembers(newMembers); + + String payload = GSON.toJson(config); + String key = composeKey(streamName, consumerGroupName); + kv.update(key, payload.getBytes(StandardCharsets.UTF_8), config.getRevision()); + + return newMembers; + } + + /** + * Removes members from an elastic consumer group. + * + * @return The updated list of members + */ + public static List deleteMembers(Connection nc, String streamName, String consumerGroupName, + List memberNamesToDrop) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + if (streamName == null || streamName.isEmpty() || + consumerGroupName == null || consumerGroupName.isEmpty() || + memberNamesToDrop == null || memberNamesToDrop.isEmpty()) { + throw new ConsumerGroupException("invalid stream name or elastic consumer group name or no member names"); + } + + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + ElasticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + if (!config.getMemberMappings().isEmpty()) { + throw new ConsumerGroupException("can't drop members from an elastic consumer group that uses member mappings"); + } + + Set droppingMembers = new HashSet<>(memberNamesToDrop); + List newMembers = new ArrayList<>(); + + for (String existingMember : config.getMembers()) { + if (!droppingMembers.contains(existingMember)) { + newMembers.add(existingMember); + } + } + + config.setMembers(newMembers); + + String payload = GSON.toJson(config); + String key = composeKey(streamName, consumerGroupName); + kv.update(key, payload.getBytes(StandardCharsets.UTF_8), config.getRevision()); + + return newMembers; + } + + /** + * Sets member mappings for an elastic consumer group. + */ + public static void setMemberMappings(Connection nc, String streamName, String consumerGroupName, + List memberMappings) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + if (streamName == null || streamName.isEmpty() || + consumerGroupName == null || consumerGroupName.isEmpty() || + memberMappings == null || memberMappings.isEmpty()) { + throw new ConsumerGroupException("invalid stream name or elastic consumer group name or member mappings"); + } + + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + ElasticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + config.setMembers(new ArrayList<>()); + config.setMemberMappings(memberMappings); + config.validate(); + + String payload = GSON.toJson(config); + String key = composeKey(streamName, consumerGroupName); + kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Deletes member mappings for an elastic consumer group. + */ + public static void deleteMemberMappings(Connection nc, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + if (streamName == null || streamName.isEmpty() || + consumerGroupName == null || consumerGroupName.isEmpty()) { + throw new ConsumerGroupException("invalid stream name or elastic consumer group name"); + } + + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + ElasticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + config.setMemberMappings(new ArrayList<>()); + + String payload = GSON.toJson(config); + String key = composeKey(streamName, consumerGroupName); + kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Lists active members of an elastic consumer group. + */ + public static List listActiveMembers(Connection nc, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + ElasticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + if (config.getMembers().isEmpty() && config.getMemberMappings().isEmpty()) { + return new ArrayList<>(); + } + + JetStreamManagement jsm = nc.jetStreamManagement(); + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + + List activeMembers = new ArrayList<>(); + List consumers = jsm.getConsumers(workQueueStreamName); + + List memberList = config.getMembers(); + List mappings = config.getMemberMappings(); + + for (ConsumerInfo cInfo : consumers) { + if (!memberList.isEmpty()) { + for (String m : memberList) { + if (cInfo.getName().equals(m)) { + activeMembers.add(m); + break; + } + } + } else if (!mappings.isEmpty()) { + for (MemberMapping mapping : mappings) { + if (cInfo.getName().equals(mapping.getMember())) { + activeMembers.add(mapping.getMember()); + break; + } + } + } + } + + return activeMembers; + } + + /** + * Checks if a member is included in the elastic consumer group and is active. + * + * @return A boolean array where [0] = inMembership, [1] = isActive + */ + public static boolean[] isInMembershipAndActive(Connection nc, String streamName, String consumerGroupName, + String memberName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + ElasticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + boolean inMembership = config.isInMembership(memberName); + + JetStreamManagement jsm = nc.jetStreamManagement(); + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + + boolean isActive = false; + List consumers = jsm.getConsumers(workQueueStreamName); + + for (ConsumerInfo cInfo : consumers) { + if (cInfo.getName().equals(memberName)) { + isActive = true; + break; + } + } + + return new boolean[]{inMembership, isActive}; + } + + /** + * Forces the current active (pinned) instance for a member to step down. + * This requires NATS server 2.11+ with priority consumer support. + */ + public static void memberStepDown(Connection nc, String streamName, String consumerGroupName, String memberName) + throws IOException, JetStreamApiException, InterruptedException { + JetStreamManagement jsm = nc.jetStreamManagement(); + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + jsm.unpinConsumer(workQueueStreamName, memberName, PRIORITY_GROUP_NAME); + } + + /** + * Gets the elastic consumer group configuration. + */ + public static ElasticConsumerGroupConfig getConfig(Connection nc, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + KeyValue kv; + try { + kv = nc.keyValue(KV_ELASTIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the elastic consumer group KV bucket doesn't exist", e); + } + + return getConfigFromKV(kv, streamName, consumerGroupName); + } + + /** + * Returns the list of partition filters for a given member based on the config. + */ + public static List getPartitionFilters(ElasticConsumerGroupConfig config, String memberName) { + return PartitionUtils.generatePartitionFilters( + config.getMembers(), config.getMaxMembers(), config.getMemberMappings(), memberName); + } + + private static ElasticConsumerGroupConfig getConfigFromKV(KeyValue kv, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + if (streamName == null || streamName.isEmpty() || + consumerGroupName == null || consumerGroupName.isEmpty()) { + throw new ConsumerGroupException("invalid stream name or elastic consumer group name"); + } + + String key = composeKey(streamName, consumerGroupName); + KeyValueEntry entry = kv.get(key); + + if (entry == null) { + throw new ConsumerGroupException("error getting the elastic consumer group's config: not found"); + } + + String json = new String(entry.getValue(), StandardCharsets.UTF_8); + ElasticConsumerGroupConfig config = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + config.setRevision(entry.getRevision()); + config.validate(); + + return config; + } + + private static String getPartitioningTransformDest(ElasticConsumerGroupConfig config) { + int[] wildcards = config.getPartitioningWildcards(); + StringBuilder wildcardList = new StringBuilder(); + for (int i = 0; i < wildcards.length; i++) { + if (i > 0) wildcardList.append(","); + wildcardList.append(wildcards[i]); + } + + String[] filterTokens = config.getFilter().split("\\."); + int cwIndex = 1; + for (int i = 0; i < filterTokens.length; i++) { + if (filterTokens[i].equals("*")) { + filterTokens[i] = "{{Wildcard(" + cwIndex + ")}}"; + cwIndex++; + } + } + + String destFromFilter = String.join(".", filterTokens); + return "{{Partition(" + config.getMaxMembers() + "," + wildcardList + ")}}." + destFromFilter; + } + + /** + * Composes the Consumer Group Stream Name. + */ + private static String composeCGSName(String streamName, String consumerGroupName) { + return streamName + "-" + consumerGroupName; + } + + /** + * Internal implementation of the consume context for elastic consumer groups. + * Uses a single event-processing thread (matching Go's instanceRoutine pattern) + * to serialize watcher updates and self-correction, avoiding race conditions. + */ + private static class ElasticConsumeContextImpl implements ConsumerGroupConsumeContext { + // NATS API error code for "filtered consumer not unique on workqueue stream" + private static final int JS_CONSUMER_WQ_CONSUMER_NOT_UNIQUE_ERR = 10100; + + private final Connection nc; + private final KeyValue kv; + private final String streamName; + private final String consumerGroupName; + private final String memberName; + private ElasticConsumerGroupConfig config; + private final Consumer handler; + private final ConsumerConfiguration consumerUserConfig; + private final CompletableFuture doneFuture; + private final AtomicBoolean stopped; + private final AtomicReference currentPinnedId; + private final long selfCorrectionIntervalMs; + + // Event queue for serializing watcher updates (like Go's keyWatcher.Updates() channel) + private final LinkedBlockingQueue eventQueue; + + // Single thread that processes all events (like Go's instanceRoutine goroutine) + private Thread instanceThread; + + // Consumer state - only accessed from instanceThread (no synchronization needed) + private MessageConsumer messageConsumer; + private io.nats.client.impl.NatsKeyValueWatchSubscription watchSubscription; + + ElasticConsumeContextImpl(Connection nc, KeyValue kv, String streamName, String consumerGroupName, + String memberName, ElasticConsumerGroupConfig config, + Consumer handler, ConsumerConfiguration consumerUserConfig) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + this.nc = nc; + this.kv = kv; + this.streamName = streamName; + this.consumerGroupName = consumerGroupName; + this.memberName = memberName; + this.config = config; + this.handler = handler; + this.consumerUserConfig = consumerUserConfig; + this.doneFuture = new CompletableFuture<>(); + this.stopped = new AtomicBoolean(false); + this.currentPinnedId = new AtomicReference<>(""); + this.eventQueue = new LinkedBlockingQueue<>(); + this.selfCorrectionIntervalMs = consumerUserConfig.getAckWait().toMillis() * CONSUMER_IDLE_TIMEOUT_FACTOR + 500; + + // Join if already in membership (before starting the routine, matching Go) + if (config.isInMembership(memberName)) { + joinMemberConsumer(); + } + + startWatcher(); + startInstanceRoutine(); + } + + /** + * Sets up the KV watcher that enqueues events to the event queue. + * The watcher callback does NO processing - it just enqueues, keeping the + * NATS dispatch thread unblocked. + */ + private void startWatcher() { + Thread watcherThread = new Thread(() -> { + try { + String key = composeKey(streamName, consumerGroupName); + KeyValueWatcher watcher = new KeyValueWatcher() { + @Override + public void watch(KeyValueEntry entry) { + if (!stopped.get()) { + eventQueue.offer(entry); + } + } + + @Override + public void endOfData() { + // Initial data load complete + } + }; + + watchSubscription = kv.watch(key, watcher, KeyValueWatchOption.UPDATES_ONLY); + + } catch (Exception e) { + if (!stopped.get()) { + doneFuture.completeExceptionally(e); + } + } + }); + watcherThread.setDaemon(true); + watcherThread.start(); + } + + /** + * Starts the single instance routine thread that processes all events. + * This matches Go's instanceRoutine goroutine with its select loop: + * - Watcher updates from the event queue + * - Self-correction via queue poll timeout + * - Stop via the stopped flag + */ + private void startInstanceRoutine() { + instanceThread = new Thread(() -> { + while (!stopped.get()) { + try { + // Poll with timeout - timeout acts as self-correction timer + // (like Go's time.After case in the select loop) + KeyValueEntry entry = eventQueue.poll(selfCorrectionIntervalMs, TimeUnit.MILLISECONDS); + + if (stopped.get()) { + break; + } + + if (entry == null) { + // Timeout = self-correction (like Go's time.After case) + if (messageConsumer == null && config.isInMembership(memberName)) { + joinMemberConsumer(); + } + } else { + // Watcher update (like Go's keyWatcher.Updates() case) + processWatcherUpdate(entry); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + + // Cleanup on exit + stopConsuming(); + doneFuture.complete(null); + }); + instanceThread.setDaemon(true); + instanceThread.start(); + } + + /** + * Processes a single watcher update event. + * Matches the watcher update case in Go's instanceRoutine select loop. + */ + private void processWatcherUpdate(KeyValueEntry entry) { + if (entry.getOperation() == KeyValueOperation.DELETE || + entry.getOperation() == KeyValueOperation.PURGE) { + stopConsuming(); + stopped.set(true); + doneFuture.complete(null); + return; + } + + try { + String json = new String(entry.getValue(), StandardCharsets.UTF_8); + ElasticConsumerGroupConfig newConfig = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + newConfig.validate(); + + // Check if critical config changed (immutable fields) + if (newConfig.getMaxMembers() != config.getMaxMembers() || + !Objects.equals(newConfig.getFilter(), config.getFilter()) || + newConfig.getMaxBufferedMsgs() != config.getMaxBufferedMsgs() || + newConfig.getMaxBufferedBytes() != config.getMaxBufferedBytes() || + !Arrays.equals(newConfig.getPartitioningWildcards(), config.getPartitioningWildcards())) { + stopConsuming(); + stopped.set(true); + doneFuture.completeExceptionally( + new ConsumerGroupException("elastic consumer group config watcher received a bad change in the configuration")); + return; + } + + // Optimization: if nothing changed and already have the consumer, skip + // (matches Go's optimization at instanceRoutine line 203-206) + if (messageConsumer != null && + Objects.equals(newConfig.getMembers(), config.getMembers()) && + Objects.equals(newConfig.getMemberMappings(), config.getMemberMappings())) { + return; + } + + // Check if members or mappings changed + if (!Objects.equals(newConfig.getMembers(), config.getMembers()) || + !Objects.equals(newConfig.getMemberMappings(), config.getMemberMappings())) { + config = newConfig; + processMembershipChange(); + } + + } catch (ConsumerGroupException e) { + stopConsuming(); + stopped.set(true); + doneFuture.completeExceptionally(e); + } catch (Exception e) { + LOGGER.warning("Error processing watcher update: " + e.getMessage()); + } + } + + /** + * Processes membership changes. Matches Go's processMembershipChange. + * Only called from instanceThread - no synchronization needed. + */ + private void processMembershipChange() { + // Check if we are the pinned member before stopping + boolean isPinned = false; + if (messageConsumer != null) { + try { + JetStreamManagement jsm = nc.jetStreamManagement(); + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + ConsumerInfo ci = jsm.getConsumerInfo(workQueueStreamName, memberName); + List pgStates = ci.getPriorityGroupStates(); + if (pgStates != null) { + String myPinnedId = currentPinnedId.get(); + for (PriorityGroupState pg : pgStates) { + if (PRIORITY_GROUP_NAME.equals(pg.getGroup()) && + myPinnedId != null && myPinnedId.equals(pg.getPinnedClientId())) { + isPinned = true; + break; + } + } + } + } catch (Exception e) { + // Ignore - consumer may not exist yet + } + // Stop the message consumer (matching Go's stopConsuming) + stopMessageConsumer(); + } + + // Only the pinned member should delete the consumer + if (isPinned) { + try { + JetStreamManagement jsm = nc.jetStreamManagement(); + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + jsm.deleteConsumer(workQueueStreamName, memberName); + } catch (Exception e) { + // Ignore - consumer may not exist + } + } else { + // Backoff to let the pinned member handle the delete and recreate first + try { + Thread.sleep(400 + (long) (Math.random() * 100)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + // Rejoin (matching Go which always calls joinMemberConsumer unconditionally) + joinMemberConsumer(); + } + + /** + * Creates or recreates the JetStream consumer and starts consuming. + * Matches Go's joinMemberConsumer. Only called from instanceThread. + */ + private void joinMemberConsumer() { + try { + List filters = ElasticConsumerGroup.getPartitionFilters(config, memberName); + + // If we are no longer in the membership list, nothing to do + if (filters.isEmpty()) { + return; + } + + JetStreamManagement jsm = nc.jetStreamManagement(); + String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + + // Build consumer configuration from user config, overriding internal fields + Duration pinnedTTL = calculatePinnedTTL(consumerUserConfig.getAckWait()); + ConsumerConfiguration cc = ConsumerConfiguration.builder(consumerUserConfig) + .durable(null) + .name(memberName) + .filterSubjects(filters) + .priorityGroups(PRIORITY_GROUP_NAME) + .priorityPolicy(PriorityPolicy.PinnedClient) + .priorityTimeout(pinnedTTL) + .build(); + + // Try to create consumer (matching Go's tryCreateConsumer pattern) + try { + jsm.createConsumer(workQueueStreamName, cc); + } catch (JetStreamApiException e) { + // Check for "filtered consumer not unique on workqueue" - silently ignore + // (normal during concurrent membership changes, self-correction will retry) + if (e.getApiErrorCode() == JS_CONSUMER_WQ_CONSUMER_NOT_UNIQUE_ERR) { + return; + } + // Consumer exists with different config - delete and recreate + // (matching Go's ErrConsumerExists handling) + try { + jsm.deleteConsumer(workQueueStreamName, memberName); + } catch (Exception deleteEx) { + LOGGER.warning("Error trying to delete consumer for member \"" + memberName + "\": " + deleteEx.getMessage()); + return; + } + try { + jsm.createConsumer(workQueueStreamName, cc); + } catch (JetStreamApiException retryEx) { + if (retryEx.getApiErrorCode() == JS_CONSUMER_WQ_CONSUMER_NOT_UNIQUE_ERR) { + return; + } + LOGGER.warning("Error trying to create consumer for member \"" + memberName + "\": " + retryEx.getMessage()); + return; + } catch (Exception retryEx) { + LOGGER.warning("Error trying to create consumer for member \"" + memberName + "\": " + retryEx.getMessage()); + return; + } + } + + // Start consuming (matching Go's startConsuming) + StreamContext sc = nc.getStreamContext(workQueueStreamName); + ConsumerContext consumerCtx = sc.getConsumerContext(memberName); + + Duration pullExpiry = calculatePullExpiry(consumerUserConfig.getAckWait()); + ConsumeOptions co = ConsumeOptions.builder() + .expiresIn(pullExpiry.toMillis()) + .group(PRIORITY_GROUP_NAME) + .build(); + + messageConsumer = consumerCtx.consume(co, msg -> { + String pid = null; + Headers headers = msg.getHeaders(); + if (headers != null) { + pid = headers.getFirst("Nats-Pin-Id"); + } + + if (pid != null && !pid.isEmpty()) { + String current = currentPinnedId.get(); + if (current.isEmpty() || !current.equals(pid)) { + currentPinnedId.set(pid); + } + } + + ConsumerGroupMsg cgMsg = new ConsumerGroupMsg(msg); + handler.accept(cgMsg); + }); + + } catch (Exception e) { + LOGGER.warning("Error joining member consumer: " + e.getMessage()); + } + } + + @Override + public void stop() { + if (stopped.compareAndSet(false, true)) { + // Interrupt the instance thread to wake it from queue.poll() + if (instanceThread != null) { + instanceThread.interrupt(); + } + } + } + + @Override + public CompletableFuture done() { + return doneFuture; + } + + /** + * Stops the JetStream message consumer by closing it (unsubscribing the pull subscription). + * Uses close() instead of stop() because: + * - stop() only sets a flag but leaves the subscription active + * - close() sets the flag AND unsubscribes the subscription from the dispatcher + * This matches Go's consumeContext.Stop() which cancels the consume loop and waits + * for exit, ensuring no pending pull requests remain when the consumer is deleted. + */ + private void stopMessageConsumer() { + if (messageConsumer != null) { + try { + messageConsumer.close(); + } catch (Exception e) { + // Ignore + } + messageConsumer = null; + } + } + + /** + * Stops everything: message consumer and KV watcher. + * Used for full shutdown only (called from instanceThread on exit). + * Matches Go's stopConsuming() + watcher cleanup. + */ + private void stopConsuming() { + stopMessageConsumer(); + if (watchSubscription != null) { + try { + watchSubscription.unsubscribe(); + } catch (Exception e) { + // Ignore + } + watchSubscription = null; + } + } + } +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java new file mode 100644 index 0000000..29e3c21 --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java @@ -0,0 +1,293 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import com.google.gson.annotations.SerializedName; +import io.synadia.pcg.exceptions.ConsumerGroupException; + +import java.util.*; + +/** + * Configuration for an elastic consumer group. + * JSON structure must be compatible with the Go version. + */ +public class ElasticConsumerGroupConfig { + + @SerializedName("max_members") + private int maxMembers; + + @SerializedName("filter") + private String filter; + + @SerializedName("partitioning_wildcards") + private int[] partitioningWildcards; + + @SerializedName("max_buffered_msg") + private long maxBufferedMsgs; + + @SerializedName("max_buffered_bytes") + private long maxBufferedBytes; + + @SerializedName("members") + private List members; + + @SerializedName("member_mappings") + private List memberMappings; + + // Internal revision number, not serialized + private transient long revision; + + public ElasticConsumerGroupConfig() { + this.partitioningWildcards = new int[0]; + this.members = new ArrayList<>(); + this.memberMappings = new ArrayList<>(); + } + + public ElasticConsumerGroupConfig(int maxMembers, String filter, int[] partitioningWildcards, + long maxBufferedMsgs, long maxBufferedBytes, + List members, List memberMappings) { + this.maxMembers = maxMembers; + this.filter = filter; + this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + this.maxBufferedMsgs = maxBufferedMsgs; + this.maxBufferedBytes = maxBufferedBytes; + this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); + this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + } + + public int getMaxMembers() { + return maxMembers; + } + + public void setMaxMembers(int maxMembers) { + this.maxMembers = maxMembers; + } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public int[] getPartitioningWildcards() { + return partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + } + + public void setPartitioningWildcards(int[] partitioningWildcards) { + this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + } + + public long getMaxBufferedMsgs() { + return maxBufferedMsgs; + } + + public void setMaxBufferedMsgs(long maxBufferedMsgs) { + this.maxBufferedMsgs = maxBufferedMsgs; + } + + public long getMaxBufferedBytes() { + return maxBufferedBytes; + } + + public void setMaxBufferedBytes(long maxBufferedBytes) { + this.maxBufferedBytes = maxBufferedBytes; + } + + public List getMembers() { + return members != null ? new ArrayList<>(members) : new ArrayList<>(); + } + + public void setMembers(List members) { + this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); + } + + public List getMemberMappings() { + return memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + } + + public void setMemberMappings(List memberMappings) { + this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + } + + public long getRevision() { + return revision; + } + + public void setRevision(long revision) { + this.revision = revision; + } + + /** + * Checks if the given member name is in the current membership. + */ + public boolean isInMembership(String name) { + if (memberMappings != null && !memberMappings.isEmpty()) { + for (MemberMapping mapping : memberMappings) { + if (mapping.getMember().equals(name)) { + return true; + } + } + } + if (members != null && !members.isEmpty()) { + return members.contains(name); + } + return false; + } + + /** + * Validates the elastic consumer group configuration. + * + * @throws ConsumerGroupException if the configuration is invalid + */ + public void validate() throws ConsumerGroupException { + // Validate max members + if (maxMembers < 1) { + throw new ConsumerGroupException("the max number of members must be >= 1"); + } + + // Validate filter and partitioning wildcards + if (filter == null || filter.isEmpty()) { + throw new ConsumerGroupException("filter must not be empty"); + } + + String[] filterTokens = filter.split("\\."); + int numWildcards = 0; + for (String token : filterTokens) { + if ("*".equals(token)) { + numWildcards++; + } + } + + if (numWildcards < 1) { + throw new ConsumerGroupException("filter must contain at least one * wildcard"); + } + + if (partitioningWildcards == null || partitioningWildcards.length < 1 || partitioningWildcards.length > numWildcards) { + throw new ConsumerGroupException("the number of partitioning wildcards must be between 1 and the total number of * wildcards in the filter"); + } + + Set seenWildcards = new HashSet<>(); + for (int pwc : partitioningWildcards) { + if (seenWildcards.contains(pwc)) { + throw new ConsumerGroupException("partitioning wildcard indexes must be unique"); + } + seenWildcards.add(pwc); + + if (pwc < 1 || pwc > numWildcards) { + throw new ConsumerGroupException("partitioning wildcard indexes must be greater than 1 and less than or equal to the number of * wildcards in the filter"); + } + } + + // Validate that only one of members or member mappings is provided + boolean hasMembers = members != null && !members.isEmpty(); + boolean hasMemberMappings = memberMappings != null && !memberMappings.isEmpty(); + + if (hasMembers && hasMemberMappings) { + throw new ConsumerGroupException("either members or member mappings must be provided, not both"); + } + + // Validate member mappings + if (hasMemberMappings) { + if (memberMappings.size() < 1 || memberMappings.size() > maxMembers) { + throw new ConsumerGroupException("the number of member mappings must be between 1 and the max number of members"); + } + + Set seenMembers = new HashSet<>(); + Set seenPartitions = new HashSet<>(); + + for (MemberMapping mm : memberMappings) { + if (seenMembers.contains(mm.getMember())) { + throw new ConsumerGroupException("member names must be unique"); + } + seenMembers.add(mm.getMember()); + + for (int p : mm.getPartitions()) { + if (seenPartitions.contains(p)) { + throw new ConsumerGroupException("partition numbers must be used only once"); + } + seenPartitions.add(p); + + if (p < 0 || p >= maxMembers) { + throw new ConsumerGroupException("partition numbers must be between 0 and one less than the max number of members"); + } + } + } + + if (seenPartitions.size() != maxMembers) { + throw new ConsumerGroupException("the number of unique partition numbers must be equal to the max number of members"); + } + } + } + + /** + * Generates the subject transform destination for partitioning. + */ + public String getPartitioningTransformDest() { + StringBuilder wildcardList = new StringBuilder(); + for (int i = 0; i < partitioningWildcards.length; i++) { + if (i > 0) { + wildcardList.append(","); + } + wildcardList.append(partitioningWildcards[i]); + } + + String[] filterTokens = filter.split("\\."); + int cwIndex = 1; + for (int i = 0; i < filterTokens.length; i++) { + if ("*".equals(filterTokens[i])) { + filterTokens[i] = "{{Wildcard(" + cwIndex + ")}}"; + cwIndex++; + } + } + + String destFromFilter = String.join(".", filterTokens); + return "{{Partition(" + maxMembers + "," + wildcardList + ")}}." + destFromFilter; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ElasticConsumerGroupConfig that = (ElasticConsumerGroupConfig) o; + return maxMembers == that.maxMembers && + maxBufferedMsgs == that.maxBufferedMsgs && + maxBufferedBytes == that.maxBufferedBytes && + Objects.equals(filter, that.filter) && + Arrays.equals(partitioningWildcards, that.partitioningWildcards) && + Objects.equals(members, that.members) && + Objects.equals(memberMappings, that.memberMappings); + } + + @Override + public int hashCode() { + int result = Objects.hash(maxMembers, filter, maxBufferedMsgs, maxBufferedBytes, members, memberMappings); + result = 31 * result + Arrays.hashCode(partitioningWildcards); + return result; + } + + @Override + public String toString() { + return "ElasticConsumerGroupConfig{" + + "maxMembers=" + maxMembers + + ", filter='" + filter + '\'' + + ", partitioningWildcards=" + Arrays.toString(partitioningWildcards) + + ", maxBufferedMsgs=" + maxBufferedMsgs + + ", maxBufferedBytes=" + maxBufferedBytes + + ", members=" + members + + ", memberMappings=" + memberMappings + + '}'; + } +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java b/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java new file mode 100644 index 0000000..adca2f3 --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java @@ -0,0 +1,78 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import com.google.gson.annotations.SerializedName; +import java.util.Arrays; +import java.util.Objects; + +/** + * Represents a mapping between a member name and its assigned partitions. + * JSON structure must be compatible with the Go version. + */ +public class MemberMapping { + + @SerializedName("member") + private String member; + + @SerializedName("partitions") + private int[] partitions; + + public MemberMapping() { + } + + public MemberMapping(String member, int[] partitions) { + this.member = member; + this.partitions = partitions != null ? partitions.clone() : new int[0]; + } + + public String getMember() { + return member; + } + + public void setMember(String member) { + this.member = member; + } + + public int[] getPartitions() { + return partitions != null ? partitions.clone() : new int[0]; + } + + public void setPartitions(int[] partitions) { + this.partitions = partitions != null ? partitions.clone() : new int[0]; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MemberMapping that = (MemberMapping) o; + return Objects.equals(member, that.member) && Arrays.equals(partitions, that.partitions); + } + + @Override + public int hashCode() { + int result = Objects.hash(member); + result = 31 * result + Arrays.hashCode(partitions); + return result; + } + + @Override + public String toString() { + return "MemberMapping{" + + "member='" + member + '\'' + + ", partitions=" + Arrays.toString(partitions) + + '}'; + } +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java b/pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java new file mode 100644 index 0000000..ddb96e1 --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java @@ -0,0 +1,169 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Utility functions shared by both static and elastic consumer groups. + * Contains constants and the partition distribution algorithm. + */ +public final class PartitionUtils { + + // Constants matching the Go implementation + public static final int PULL_TIMEOUT_DIVIDER = 2; + public static final int CONSUMER_IDLE_TIMEOUT_FACTOR = 1; + public static final Duration DEFAULT_ACK_WAIT = Duration.ofSeconds(5); + public static final Duration MIN_PULL_EXPIRY_PINNED_TTL = Duration.ofSeconds(1); + public static final String PRIORITY_GROUP_NAME = "PCG"; + + // KV bucket names (must match Go implementation) + public static final String KV_STATIC_BUCKET_NAME = "static-consumer-groups"; + public static final String KV_ELASTIC_BUCKET_NAME = "elastic-consumer-groups"; + + private PartitionUtils() { + // Utility class + } + + /** + * Compose the consumer group's config key name. + * Format: "{streamName}.{consumerGroupName}" + */ + public static String composeKey(String streamName, String consumerGroupName) { + return streamName + "." + consumerGroupName; + } + + /** + * Compose the consumer group stream name for elastic consumer groups. + * Format: "{streamName}-{consumerGroupName}" + */ + public static String composeCGSName(String streamName, String consumerGroupName) { + return streamName + "-" + consumerGroupName; + } + + /** + * Compose the static consumer name. + * Format: "{consumerGroupName}-{memberName}" + */ + public static String composeStaticConsumerName(String consumerGroupName, String memberName) { + return consumerGroupName + "-" + memberName; + } + + /** + * Generates the partition filters for a particular member of a consumer group, + * according to the provided max number of members and the membership. + * This algorithm must match the Go implementation exactly for wire compatibility. + * + * @param members List of member names (for balanced distribution) + * @param maxMembers Maximum number of members/partitions + * @param memberMappings Explicit member-to-partition mappings (alternative to members) + * @param memberName The member to generate filters for + * @return List of partition filters (e.g., "0.>", "1.>", "2.>") + */ + public static List generatePartitionFilters(List members, int maxMembers, + List memberMappings, String memberName) { + if (members != null && !members.isEmpty()) { + // Deduplicate and sort members + List sortedMembers = members.stream() + .distinct() + .sorted() + .collect(Collectors.toList()); + + // Cap to maxMembers if necessary + if (sortedMembers.size() > maxMembers) { + sortedMembers = sortedMembers.subList(0, maxMembers); + } + + int numMembers = sortedMembers.size(); + + if (numMembers > 0) { + // Rounded number of partitions per member + int numPer = maxMembers / numMembers; + List myFilters = new ArrayList<>(); + + for (int i = 0; i < maxMembers; i++) { + int memberIndex = i / numPer; + + if (i < (numMembers * numPer)) { + if (sortedMembers.get(memberIndex % numMembers).equals(memberName)) { + myFilters.add(i + ".>"); + } + } else { + // Remainder if the number of partitions is not a multiple of the number of members + if (sortedMembers.get((i - (numMembers * numPer)) % numMembers).equals(memberName)) { + myFilters.add(i + ".>"); + } + } + } + + return myFilters; + } + return Collections.emptyList(); + } else if (memberMappings != null && !memberMappings.isEmpty()) { + List myFilters = new ArrayList<>(); + + for (MemberMapping mapping : memberMappings) { + if (mapping.getMember().equals(memberName)) { + for (int pn : mapping.getPartitions()) { + myFilters.add(pn + ".>"); + } + } + } + + return myFilters; + } + return Collections.emptyList(); + } + + /** + * Deduplicate a list of strings while preserving order. + */ + public static List deduplicateStringList(List list) { + if (list == null) { + return Collections.emptyList(); + } + Set seen = new LinkedHashSet<>(); + List result = new ArrayList<>(); + for (String s : list) { + if (seen.add(s)) { + result.add(s); + } + } + return result; + } + + /** + * Calculate the pull expiry duration based on ack wait. + */ + public static Duration calculatePullExpiry(Duration ackWait) { + Duration calculated = ackWait.dividedBy(PULL_TIMEOUT_DIVIDER); + return calculated.compareTo(MIN_PULL_EXPIRY_PINNED_TTL) > 0 ? calculated : MIN_PULL_EXPIRY_PINNED_TTL; + } + + /** + * Calculate the pinned TTL based on ack wait. + */ + public static Duration calculatePinnedTTL(Duration ackWait) { + return ackWait.compareTo(MIN_PULL_EXPIRY_PINNED_TTL) > 0 ? ackWait : MIN_PULL_EXPIRY_PINNED_TTL; + } + + /** + * Calculate the inactive threshold based on ack wait. + */ + public static Duration calculateInactiveThreshold(Duration ackWait) { + return ackWait.multipliedBy(CONSUMER_IDLE_TIMEOUT_FACTOR); + } +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java new file mode 100644 index 0000000..a9f5d15 --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java @@ -0,0 +1,508 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import io.nats.client.*; +import io.nats.client.api.*; +import io.nats.client.impl.Headers; +import io.synadia.pcg.exceptions.ConsumerGroupException; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import static io.synadia.pcg.PartitionUtils.*; + +/** + * Static consumer group implementation. + * Provides static partition assignment for consumer groups. + */ +public class StaticConsumerGroup { + + private static final Logger LOGGER = Logger.getLogger(StaticConsumerGroup.class.getName()); + private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + + private StaticConsumerGroup() { + // Utility class + } + + /** + * Creates a static consumer group. + * + * @param nc NATS connection + * @param streamName Name of the stream + * @param consumerGroupName Name of the consumer group + * @param maxMembers Maximum number of members (partitions) + * @param filter Subject filter + * @param members List of member names (for balanced distribution) + * @param memberMappings Explicit member-to-partition mappings + * @return The created configuration + */ + public static StaticConsumerGroupConfig create(Connection nc, String streamName, String consumerGroupName, + int maxMembers, String filter, List members, + List memberMappings) throws ConsumerGroupException, IOException, JetStreamApiException, InterruptedException { + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig(maxMembers, filter, members, memberMappings); + config.validate(); + + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Get stream info to determine replicas + StreamInfo streamInfo = jsm.getStreamInfo(streamName); + int replicas = streamInfo.getConfiguration().getReplicas(); + + // Get or create the KV bucket + KeyValueManagement kvm = nc.keyValueManagement(); + KeyValue kv; + try { + kv = nc.keyValue(KV_STATIC_BUCKET_NAME); + } catch (Exception e) { + // Create the bucket if it doesn't exist + KeyValueConfiguration kvConfig = KeyValueConfiguration.builder() + .name(KV_STATIC_BUCKET_NAME) + .replicas(replicas) + .storageType(StorageType.File) + .build(); + kvm.create(kvConfig); + kv = nc.keyValue(KV_STATIC_BUCKET_NAME); + } + + String key = composeKey(streamName, consumerGroupName); + + // Check if config already exists + KeyValueEntry entry = kv.get(key); + if (entry != null) { + String json = new String(entry.getValue(), StandardCharsets.UTF_8); + StaticConsumerGroupConfig existingConfig = GSON.fromJson(json, StaticConsumerGroupConfig.class); + + // Verify the config matches + if (!configsMatch(existingConfig, config)) { + throw new ConsumerGroupException("the existing static consumer group config doesn't match ours"); + } + return existingConfig; + } + + // Create the config entry + String payload = GSON.toJson(config); + kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + + return config; + } + + /** + * Starts consuming messages from a static consumer group. + * + * @param nc NATS connection + * @param streamName Name of the stream + * @param consumerGroupName Name of the consumer group + * @param memberName Name of this member + * @param handler Message handler callback + * @param consumerConfig Consumer configuration (null for defaults) + * @return A consume context for controlling the consumption lifecycle + */ + public static ConsumerGroupConsumeContext consume(Connection nc, String streamName, String consumerGroupName, + String memberName, Consumer handler, + ConsumerConfiguration consumerConfig) throws ConsumerGroupException, IOException, JetStreamApiException, InterruptedException { + if (handler == null) { + throw new ConsumerGroupException("a message handler must be provided"); + } + + if (consumerConfig == null) { + consumerConfig = ConsumerConfiguration.builder().ackWait(DEFAULT_ACK_WAIT).build(); + } else if (consumerConfig.getAckWait() == null || consumerConfig.getAckWait().isZero() || consumerConfig.getAckWait().isNegative()) { + consumerConfig = ConsumerConfiguration.builder(consumerConfig).ackWait(DEFAULT_ACK_WAIT).build(); + } + + // Get the KV bucket + KeyValue kv; + try { + kv = nc.keyValue(KV_STATIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the static consumer group KV bucket doesn't exist", e); + } + + // Get the config + StaticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + + if (!config.isInMembership(memberName)) { + throw new ConsumerGroupException("the member name is not in the current static consumer group membership"); + } + + // Verify stream exists + JetStreamManagement jsm = nc.jetStreamManagement(); + try { + jsm.getStreamInfo(streamName); + } catch (JetStreamApiException e) { + throw new ConsumerGroupException("the static consumer group's stream does not exist", e); + } + + return new StaticConsumeContextImpl(nc, kv, streamName, consumerGroupName, memberName, config, handler, consumerConfig); + } + + /** + * Deletes a static consumer group. + */ + public static void delete(Connection nc, String streamName, String consumerGroupName) throws IOException, JetStreamApiException, InterruptedException { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Get the KV bucket + KeyValue kv; + try { + kv = nc.keyValue(KV_STATIC_BUCKET_NAME); + } catch (Exception e) { + return; // Bucket doesn't exist + } + + // Delete the config entry + String key = composeKey(streamName, consumerGroupName); + try { + kv.delete(key); + } catch (Exception e) { + // Ignore if key doesn't exist + } + + // Delete consumers that match the pattern + List consumerNames = jsm.getConsumerNames(streamName); + + for (String consumerName : consumerNames) { + if (consumerName.startsWith(consumerGroupName + "-")) { + try { + jsm.deleteConsumer(streamName, consumerName); + } catch (JetStreamApiException e) { + // Ignore if consumer doesn't exist + } + } + } + } + + /** + * Lists static consumer groups for a stream. + */ + public static List list(Connection nc, String streamName) throws IOException, JetStreamApiException, InterruptedException { + KeyValue kv; + try { + kv = nc.keyValue(KV_STATIC_BUCKET_NAME); + } catch (Exception e) { + return new ArrayList<>(); + } + + List keys = kv.keys(); + List consumerGroupNames = new ArrayList<>(); + + for (String key : keys) { + String[] parts = key.split("\\."); + if (parts.length >= 2 && parts[0].equals(streamName)) { + consumerGroupNames.add(parts[1]); + } + } + + return consumerGroupNames; + } + + /** + * Lists active members of a static consumer group. + */ + public static List listActiveMembers(Connection nc, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + KeyValue kv; + try { + kv = nc.keyValue(KV_STATIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the static consumer group KV bucket doesn't exist", e); + } + + StaticConsumerGroupConfig config = getConfigFromKV(kv, streamName, consumerGroupName); + JetStreamManagement jsm = nc.jetStreamManagement(); + + List activeMembers = new ArrayList<>(); + List consumers = jsm.getConsumers(streamName); + + List memberList = config.getMembers(); + List mappings = config.getMemberMappings(); + + for (ConsumerInfo cInfo : consumers) { + if (!memberList.isEmpty()) { + for (String m : memberList) { + if (cInfo.getName().equals(composeStaticConsumerName(consumerGroupName, m)) && cInfo.getNumWaiting() > 0) { + activeMembers.add(m); + break; + } + } + } else if (!mappings.isEmpty()) { + for (MemberMapping mapping : mappings) { + if (cInfo.getName().equals(composeStaticConsumerName(consumerGroupName, mapping.getMember())) && cInfo.getNumWaiting() > 0) { + activeMembers.add(mapping.getMember()); + break; + } + } + } + } + + return activeMembers; + } + + /** + * Forces the current active (pinned) instance for a member to step down. + * This requires NATS server 2.11+ with priority consumer support. + */ + public static void memberStepDown(Connection nc, String streamName, String consumerGroupName, String memberName) + throws IOException, JetStreamApiException, InterruptedException { + JetStreamManagement jsm = nc.jetStreamManagement(); + String consumerName = composeStaticConsumerName(consumerGroupName, memberName); + jsm.unpinConsumer(streamName, consumerName, PRIORITY_GROUP_NAME); + } + + /** + * Gets the static consumer group configuration. + */ + public static StaticConsumerGroupConfig getConfig(Connection nc, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + KeyValue kv; + try { + kv = nc.keyValue(KV_STATIC_BUCKET_NAME); + } catch (Exception e) { + throw new ConsumerGroupException("the static consumer group KV bucket doesn't exist", e); + } + + return getConfigFromKV(kv, streamName, consumerGroupName); + } + + private static StaticConsumerGroupConfig getConfigFromKV(KeyValue kv, String streamName, String consumerGroupName) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + if (streamName == null || streamName.isEmpty() || consumerGroupName == null || consumerGroupName.isEmpty()) { + throw new ConsumerGroupException("invalid stream name or consumer group name"); + } + + String key = composeKey(streamName, consumerGroupName); + KeyValueEntry entry = kv.get(key); + + if (entry == null) { + throw new ConsumerGroupException("error getting the static consumer group's config: not found"); + } + + String json = new String(entry.getValue(), StandardCharsets.UTF_8); + StaticConsumerGroupConfig config = GSON.fromJson(json, StaticConsumerGroupConfig.class); + config.validate(); + + return config; + } + + private static boolean configsMatch(StaticConsumerGroupConfig a, StaticConsumerGroupConfig b) { + return a.getMaxMembers() == b.getMaxMembers() && + java.util.Objects.equals(a.getFilter(), b.getFilter()) && + java.util.Objects.equals(a.getMembers(), b.getMembers()) && + java.util.Objects.equals(a.getMemberMappings(), b.getMemberMappings()); + } + + /** + * Internal implementation of the consume context for static consumer groups. + */ + private static class StaticConsumeContextImpl implements ConsumerGroupConsumeContext { + private final Connection nc; + private final KeyValue kv; + private final String streamName; + private final String consumerGroupName; + private final String memberName; + private final StaticConsumerGroupConfig config; + private final Consumer handler; + private final ConsumerConfiguration consumerUserConfig; + private final CompletableFuture doneFuture; + private final AtomicBoolean stopped; + private final AtomicReference currentPinnedId; + private MessageConsumer messageConsumer; + private io.nats.client.impl.NatsKeyValueWatchSubscription watchSubscription; + + StaticConsumeContextImpl(Connection nc, KeyValue kv, String streamName, String consumerGroupName, + String memberName, StaticConsumerGroupConfig config, + Consumer handler, ConsumerConfiguration consumerUserConfig) + throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + this.nc = nc; + this.kv = kv; + this.streamName = streamName; + this.consumerGroupName = consumerGroupName; + this.memberName = memberName; + this.config = config; + this.handler = handler; + this.consumerUserConfig = consumerUserConfig; + this.doneFuture = new CompletableFuture<>(); + this.stopped = new AtomicBoolean(false); + this.currentPinnedId = new AtomicReference<>(""); + + joinMemberConsumer(); + startWatcher(); + } + + private void joinMemberConsumer() throws IOException, JetStreamApiException, InterruptedException, ConsumerGroupException { + List filters = PartitionUtils.generatePartitionFilters( + config.getMembers(), config.getMaxMembers(), config.getMemberMappings(), memberName); + + if (filters.isEmpty()) { + return; + } + + String consumerName = composeStaticConsumerName(consumerGroupName, memberName); + + // Build consumer configuration from user config, overriding internal fields + Duration pinnedTTL = calculatePinnedTTL(consumerUserConfig.getAckWait()); + ConsumerConfiguration cc = ConsumerConfiguration.builder(consumerUserConfig) + .durable(consumerName) + .name(consumerName) // Needed for cross-compatibility with Go client which creates the consumer using another JS API call + .filterSubjects(filters) + .priorityGroups(PRIORITY_GROUP_NAME) + .priorityPolicy(PriorityPolicy.PinnedClient) + .priorityTimeout(pinnedTTL) + .build(); + + // Create the durable consumer explicitly (matching Go's js.CreateConsumer) + JetStreamManagement jsm = nc.jetStreamManagement(); + System.out.printf("Creating consumer %s with filters %s and priority group %s%n\n", consumerName, filters, PRIORITY_GROUP_NAME); + jsm.createConsumer(streamName, cc); + + // Get consumer context and start consuming + StreamContext sc = nc.getStreamContext(streamName); + ConsumerContext consumerCtx = sc.getConsumerContext(consumerName); + + Duration pullExpiry = calculatePullExpiry(consumerUserConfig.getAckWait()); + ConsumeOptions co = ConsumeOptions.builder() + .expiresIn(pullExpiry.toMillis()) + .group(PRIORITY_GROUP_NAME) + .build(); + + messageConsumer = consumerCtx.consume(co, msg -> { + String pid = null; + Headers headers = msg.getHeaders(); + if (headers != null) { + pid = headers.getFirst("Nats-Pin-Id"); + } + + if (pid != null && !pid.isEmpty()) { + String current = currentPinnedId.get(); + if (current.isEmpty() || !current.equals(pid)) { + currentPinnedId.set(pid); + } + } + + ConsumerGroupMsg cgMsg = new ConsumerGroupMsg(msg); + handler.accept(cgMsg); + }); + } + + private void startWatcher() { + Thread watcherThread = new Thread(() -> { + try { + String key = composeKey(streamName, consumerGroupName); + KeyValueWatcher watcher = new KeyValueWatcher() { + @Override + public void watch(KeyValueEntry entry) { + if (stopped.get()) { + return; + } + + if (entry.getOperation() == KeyValueOperation.DELETE || + entry.getOperation() == KeyValueOperation.PURGE) { + stopAndDeleteMemberConsumer(); + doneFuture.complete(null); + return; + } + + try { + String json = new String(entry.getValue(), StandardCharsets.UTF_8); + StaticConsumerGroupConfig newConfig = GSON.fromJson(json, StaticConsumerGroupConfig.class); + newConfig.validate(); + + // Check if critical config changed + if (newConfig.getMaxMembers() != config.getMaxMembers() || + !java.util.Objects.equals(newConfig.getFilter(), config.getFilter()) || + !java.util.Objects.equals(newConfig.getMembers(), config.getMembers()) || + !java.util.Objects.equals(newConfig.getMemberMappings(), config.getMemberMappings())) { + stopAndDeleteMemberConsumer(); + doneFuture.completeExceptionally( + new ConsumerGroupException("static consumer group config watcher received a change in the configuration, terminating")); + } + } catch (Exception e) { + stopAndDeleteMemberConsumer(); + doneFuture.completeExceptionally(e); + } + } + + @Override + public void endOfData() { + // Initial data load complete + } + }; + + watchSubscription = kv.watch(key, watcher, KeyValueWatchOption.UPDATES_ONLY); + + } catch (Exception e) { + if (!stopped.get()) { + doneFuture.completeExceptionally(e); + } + } + }); + watcherThread.setDaemon(true); + watcherThread.start(); + } + + @Override + public void stop() { + if (stopped.compareAndSet(false, true)) { + stopConsuming(); + doneFuture.complete(null); + } + } + + @Override + public CompletableFuture done() { + return doneFuture; + } + + private void stopConsuming() { + if (messageConsumer != null) { + try { + messageConsumer.close(); + } catch (Exception e) { + // Ignore + } + messageConsumer = null; + } + if (watchSubscription != null) { + try { + watchSubscription.unsubscribe(); + } catch (Exception e) { + // Ignore + } + watchSubscription = null; + } + } + + private void stopAndDeleteMemberConsumer() { + stopConsuming(); + try { + JetStreamManagement jsm = nc.jetStreamManagement(); + String consumerName = composeStaticConsumerName(consumerGroupName, memberName); + jsm.deleteConsumer(streamName, consumerName); + } catch (Exception e) { + // Ignore - consumer may not exist + } + } + } +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java new file mode 100644 index 0000000..3f0ad2d --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java @@ -0,0 +1,177 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import com.google.gson.annotations.SerializedName; +import io.synadia.pcg.exceptions.ConsumerGroupException; + +import java.util.*; + +/** + * Configuration for a static consumer group. + * JSON structure must be compatible with the Go version. + */ +public class StaticConsumerGroupConfig { + + @SerializedName("max_members") + private int maxMembers; + + @SerializedName("filter") + private String filter; + + @SerializedName("members") + private List members; + + @SerializedName("member_mappings") + private List memberMappings; + + public StaticConsumerGroupConfig() { + this.members = new ArrayList<>(); + this.memberMappings = new ArrayList<>(); + } + + public StaticConsumerGroupConfig(int maxMembers, String filter, List members, List memberMappings) { + this.maxMembers = maxMembers; + this.filter = filter; + this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); + this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + } + + public int getMaxMembers() { + return maxMembers; + } + + public void setMaxMembers(int maxMembers) { + this.maxMembers = maxMembers; + } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public List getMembers() { + return members != null ? new ArrayList<>(members) : new ArrayList<>(); + } + + public void setMembers(List members) { + this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); + } + + public List getMemberMappings() { + return memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + } + + public void setMemberMappings(List memberMappings) { + this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + } + + /** + * Checks if the given member name is in the current membership. + */ + public boolean isInMembership(String name) { + if (memberMappings != null && !memberMappings.isEmpty()) { + for (MemberMapping mapping : memberMappings) { + if (mapping.getMember().equals(name)) { + return true; + } + } + } + if (members != null && !members.isEmpty()) { + return members.contains(name); + } + return false; + } + + /** + * Validates the static consumer group configuration. + * + * @throws ConsumerGroupException if the configuration is invalid + */ + public void validate() throws ConsumerGroupException { + // Validate max members + if (maxMembers < 1) { + throw new ConsumerGroupException("the max number of members must be >= 1"); + } + + // Validate that only one of members or member mappings is provided + boolean hasMembers = members != null && !members.isEmpty(); + boolean hasMemberMappings = memberMappings != null && !memberMappings.isEmpty(); + + if (hasMembers && hasMemberMappings) { + throw new ConsumerGroupException("either members or member mappings must be provided, not both"); + } + + // Validate member mappings + if (hasMemberMappings) { + if (memberMappings.size() < 1 || memberMappings.size() > maxMembers) { + throw new ConsumerGroupException("the number of member mappings must be between 1 and the max number of members"); + } + + Set seenMembers = new HashSet<>(); + Set seenPartitions = new HashSet<>(); + + for (MemberMapping mm : memberMappings) { + if (seenMembers.contains(mm.getMember())) { + throw new ConsumerGroupException("member names must be unique"); + } + seenMembers.add(mm.getMember()); + + for (int p : mm.getPartitions()) { + if (seenPartitions.contains(p)) { + throw new ConsumerGroupException("partition numbers must be used only once"); + } + seenPartitions.add(p); + + if (p < 0 || p >= maxMembers) { + throw new ConsumerGroupException("partition numbers must be between 0 and one less than the max number of members"); + } + } + } + + if (seenPartitions.size() != maxMembers) { + throw new ConsumerGroupException("the number of unique partition numbers must be equal to the max number of members"); + } + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StaticConsumerGroupConfig that = (StaticConsumerGroupConfig) o; + return maxMembers == that.maxMembers && + Objects.equals(filter, that.filter) && + Objects.equals(members, that.members) && + Objects.equals(memberMappings, that.memberMappings); + } + + @Override + public int hashCode() { + return Objects.hash(maxMembers, filter, members, memberMappings); + } + + @Override + public String toString() { + return "StaticConsumerGroupConfig{" + + "maxMembers=" + maxMembers + + ", filter='" + filter + '\'' + + ", members=" + members + + ", memberMappings=" + memberMappings + + '}'; + } +} diff --git a/pcgroups/src/main/java/io/synadia/pcg/exceptions/ConsumerGroupException.java b/pcgroups/src/main/java/io/synadia/pcg/exceptions/ConsumerGroupException.java new file mode 100644 index 0000000..58953ef --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/exceptions/ConsumerGroupException.java @@ -0,0 +1,32 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg.exceptions; + +/** + * Exception thrown when a consumer group operation fails. + */ +public class ConsumerGroupException extends Exception { + + public ConsumerGroupException(String message) { + super(message); + } + + public ConsumerGroupException(String message, Throwable cause) { + super(message, cause); + } + + public ConsumerGroupException(Throwable cause) { + super(cause); + } +} diff --git a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java new file mode 100644 index 0000000..e4a9be3 --- /dev/null +++ b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java @@ -0,0 +1,390 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import io.synadia.pcg.exceptions.ConsumerGroupException; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for ElasticConsumerGroupConfig. + * Tests config validation, JSON serialization, and partition transform destination generation. + */ +class ElasticConsumerGroupTest { + + private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + + @Test + void testConfigBasic() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 1000, 10000, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + assertEquals(4, config.getMaxMembers()); + assertEquals("foo.*", config.getFilter()); + assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); + assertEquals(1000, config.getMaxBufferedMsgs()); + assertEquals(10000, config.getMaxBufferedBytes()); + assertEquals(2, config.getMembers().size()); + assertTrue(config.getMemberMappings().isEmpty()); + } + + @Test + void testIsInMembership() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 0, 0, + Arrays.asList("m1", "m2", "m3"), new ArrayList<>() + ); + + assertTrue(config.isInMembership("m1")); + assertTrue(config.isInMembership("m2")); + assertTrue(config.isInMembership("m3")); + assertFalse(config.isInMembership("m4")); + } + + @Test + void testIsInMembershipWithMappings() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2, 3}) + ); + + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 0, 0, + new ArrayList<>(), mappings + ); + + assertTrue(config.isInMembership("alice")); + assertTrue(config.isInMembership("bob")); + assertFalse(config.isInMembership("charlie")); + } + + @Test + void testValidationMaxMembersZero() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 0, "foo.*", new int[]{1}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("max number of members must be >= 1")); + } + + @Test + void testValidationFilterNoWildcard() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.bar", new int[]{1}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("filter must contain at least one * wildcard")); + } + + @Test + void testValidationPartitioningWildcardsEmpty() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("number of partitioning wildcards must be between")); + } + + @Test + void testValidationPartitioningWildcardsTooMany() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1, 2}, 0, 0, // Only 1 wildcard in filter + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("number of partitioning wildcards must be between")); + } + + @Test + void testValidationPartitioningWildcardsOutOfRange() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{2}, 0, 0, // Index 2 is out of range (only 1 wildcard) + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("partitioning wildcard indexes must be greater than 1")); + } + + @Test + void testValidationPartitioningWildcardsDuplicate() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*.bar.*", new int[]{1, 1}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("partitioning wildcard indexes must be unique")); + } + + @Test + void testValidationBothMembersAndMappings() { + List mappings = Collections.singletonList( + new MemberMapping("alice", new int[]{0, 1, 2, 3}) + ); + + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 0, 0, + Arrays.asList("m1", "m2"), mappings + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("either members or member mappings must be provided, not both")); + } + + @Test + void testValidationSuccess() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + assertDoesNotThrow(config::validate); + } + + @Test + void testValidationSuccessWithMappings() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2, 3}) + ); + + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 0, 0, + new ArrayList<>(), mappings + ); + + assertDoesNotThrow(config::validate); + } + + @Test + void testValidationSuccessMultipleWildcards() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*.bar.*", new int[]{1, 2}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + assertDoesNotThrow(config::validate); + } + + @Test + void testGetPartitioningTransformDestSingle() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 0, 0, + new ArrayList<>(), new ArrayList<>() + ); + + String dest = config.getPartitioningTransformDest(); + assertEquals("{{Partition(4,1)}}.foo.{{Wildcard(1)}}", dest); + } + + @Test + void testGetPartitioningTransformDestMultiple() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 6, "foo.*.bar.*", new int[]{1, 2}, 0, 0, + new ArrayList<>(), new ArrayList<>() + ); + + String dest = config.getPartitioningTransformDest(); + assertEquals("{{Partition(6,1,2)}}.foo.{{Wildcard(1)}}.bar.{{Wildcard(2)}}", dest); + } + + @Test + void testGetPartitioningTransformDestPartialWildcards() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 8, "a.*.b.*.c.*", new int[]{2}, 0, 0, + new ArrayList<>(), new ArrayList<>() + ); + + String dest = config.getPartitioningTransformDest(); + assertEquals("{{Partition(8,2)}}.a.{{Wildcard(1)}}.b.{{Wildcard(2)}}.c.{{Wildcard(3)}}", dest); + } + + @Test + void testJsonSerializationWithMembers() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 1000, 10000, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + String json = GSON.toJson(config); + + // Verify JSON structure matches Go format + assertTrue(json.contains("\"max_members\":4")); + assertTrue(json.contains("\"filter\":\"foo.*\"")); + assertTrue(json.contains("\"partitioning_wildcards\":[1]")); + assertTrue(json.contains("\"max_buffered_msg\":1000")); + assertTrue(json.contains("\"max_buffered_bytes\":10000")); + assertTrue(json.contains("\"members\":[\"m1\",\"m2\"]")); + + // Deserialize and verify + ElasticConsumerGroupConfig deserialized = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + assertEquals(config.getMaxMembers(), deserialized.getMaxMembers()); + assertEquals(config.getFilter(), deserialized.getFilter()); + assertArrayEquals(config.getPartitioningWildcards(), deserialized.getPartitioningWildcards()); + assertEquals(config.getMaxBufferedMsgs(), deserialized.getMaxBufferedMsgs()); + assertEquals(config.getMaxBufferedBytes(), deserialized.getMaxBufferedBytes()); + assertEquals(config.getMembers(), deserialized.getMembers()); + } + + @Test + void testJsonSerializationWithMappings() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2, 3}) + ); + + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 0, 0, + new ArrayList<>(), mappings + ); + + String json = GSON.toJson(config); + + assertTrue(json.contains("\"member_mappings\"")); + assertTrue(json.contains("\"member\":\"alice\"")); + assertTrue(json.contains("\"partitions\":[0,1]")); + + // Deserialize and verify + ElasticConsumerGroupConfig deserialized = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + assertEquals(2, deserialized.getMemberMappings().size()); + assertEquals("alice", deserialized.getMemberMappings().get(0).getMember()); + assertArrayEquals(new int[]{0, 1}, deserialized.getMemberMappings().get(0).getPartitions()); + } + + @Test + void testJsonDeserializationFromGo() { + // This JSON is in the format produced by the Go implementation + String goJson = "{\"max_members\":4,\"filter\":\"foo.*\",\"partitioning_wildcards\":[1],\"max_buffered_msg\":1000,\"max_buffered_bytes\":10000,\"members\":[\"m1\",\"m2\"]}"; + + ElasticConsumerGroupConfig config = GSON.fromJson(goJson, ElasticConsumerGroupConfig.class); + + assertEquals(4, config.getMaxMembers()); + assertEquals("foo.*", config.getFilter()); + assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); + assertEquals(1000, config.getMaxBufferedMsgs()); + assertEquals(10000, config.getMaxBufferedBytes()); + assertEquals(2, config.getMembers().size()); + assertEquals("m1", config.getMembers().get(0)); + assertEquals("m2", config.getMembers().get(1)); + } + + @Test + void testJsonDeserializationWithMappingsFromGo() { + // This JSON is in the format produced by the Go implementation + String goJson = "{\"max_members\":4,\"filter\":\"bar.*\",\"partitioning_wildcards\":[1],\"member_mappings\":[{\"member\":\"alice\",\"partitions\":[0,1]},{\"member\":\"bob\",\"partitions\":[2,3]}]}"; + + ElasticConsumerGroupConfig config = GSON.fromJson(goJson, ElasticConsumerGroupConfig.class); + + assertEquals(4, config.getMaxMembers()); + assertEquals("bar.*", config.getFilter()); + assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); + assertEquals(2, config.getMemberMappings().size()); + assertEquals("alice", config.getMemberMappings().get(0).getMember()); + assertArrayEquals(new int[]{0, 1}, config.getMemberMappings().get(0).getPartitions()); + assertEquals("bob", config.getMemberMappings().get(1).getMember()); + assertArrayEquals(new int[]{2, 3}, config.getMemberMappings().get(1).getPartitions()); + } + + @Test + void testEquals() { + ElasticConsumerGroupConfig config1 = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 1000, 10000, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ElasticConsumerGroupConfig config2 = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 1000, 10000, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ElasticConsumerGroupConfig config3 = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 1000, 10000, + Arrays.asList("m1", "m3"), new ArrayList<>() + ); + + assertEquals(config1, config2); + assertNotEquals(config1, config3); + } + + @Test + void testHashCode() { + ElasticConsumerGroupConfig config1 = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 1000, 10000, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ElasticConsumerGroupConfig config2 = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{1}, 1000, 10000, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + assertEquals(config1.hashCode(), config2.hashCode()); + } + + @Test + void testRevision() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig(); + assertEquals(0, config.getRevision()); + + config.setRevision(42); + assertEquals(42, config.getRevision()); + + // Verify revision is not serialized + String json = GSON.toJson(config); + assertFalse(json.contains("revision")); + } + + @Test + void testGetPartitionFilters() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 6, "foo.*", new int[]{1}, 0, 0, + Arrays.asList("m1", "m2", "m3"), new ArrayList<>() + ); + + List m1Filters = ElasticConsumerGroup.getPartitionFilters(config, "m1"); + List m2Filters = ElasticConsumerGroup.getPartitionFilters(config, "m2"); + List m3Filters = ElasticConsumerGroup.getPartitionFilters(config, "m3"); + + // Each member should get 2 partitions + assertEquals(2, m1Filters.size()); + assertEquals(2, m2Filters.size()); + assertEquals(2, m3Filters.size()); + + // Verify distribution + assertTrue(m1Filters.contains("0.>")); + assertTrue(m1Filters.contains("1.>")); + assertTrue(m2Filters.contains("2.>")); + assertTrue(m2Filters.contains("3.>")); + assertTrue(m3Filters.contains("4.>")); + assertTrue(m3Filters.contains("5.>")); + } +} diff --git a/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java new file mode 100644 index 0000000..c48a3de --- /dev/null +++ b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java @@ -0,0 +1,238 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import io.nats.NatsServerRunner; +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.api.ConsumerConfiguration; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamConfiguration; +import io.nats.client.api.SubjectTransform; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for Static and Elastic consumer groups. + * Ported from Go: golang/test/stream_consumer_group_test.go + */ +class IntegrationTest { + + /** + * Ported from Go TestStatic. + * Creates a stream with subject transform, publishes 10 messages, + * and verifies 2 static members consume all messages. + */ + @Test + void testStatic() throws Exception { + try (NatsServerRunner server = NatsServerRunner.builder().jetstream(true).build()) { + Connection nc = Nats.connect(server.getNatsLocalhostUri()); + + String streamName = "test"; + String cgName = "group"; + AtomicInteger c1 = new AtomicInteger(0); + AtomicInteger c2 = new AtomicInteger(0); + + // Create a stream with subject transform (like the Go test) + nc.jetStreamManagement().addStream(StreamConfiguration.builder() + .name(streamName) + .subjects("bar.*") + .subjectTransform(SubjectTransform.builder() + .source("bar.*") + .destination("{{partition(2,1)}}.bar.{{wildcard(1)}}") + .build()) + .storageType(StorageType.Memory) + .build()); + + // Publish 10 messages + for (int i = 0; i < 10; i++) { + nc.jetStream().publish("bar." + i, "payload".getBytes()); + } + + ConsumerConfiguration config = ConsumerConfiguration.builder() + .maxAckPending(1) + .ackWait(Duration.ofSeconds(1)) + .build(); + + // Create static consumer group + StaticConsumerGroup.create(nc, streamName, cgName, 2, "bar.*", + Arrays.asList("m1", "m2"), new ArrayList<>()); + + // Start consuming on both members + ConsumerGroupConsumeContext cc1 = StaticConsumerGroup.consume(nc, streamName, cgName, "m1", msg -> { + c1.incrementAndGet(); + try { + msg.ack(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, config); + + ConsumerGroupConsumeContext cc2 = StaticConsumerGroup.consume(nc, streamName, cgName, "m2", msg -> { + c2.incrementAndGet(); + try { + msg.ack(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, config); + + // Wait for all 10 messages to be consumed + long deadline = System.currentTimeMillis() + 5000; + while (c1.get() + c2.get() < 10) { + Thread.sleep(100); + if (System.currentTimeMillis() > deadline) { + fail("timeout: c1=" + c1.get() + " c2=" + c2.get() + " expected total=10"); + } + } + + assertEquals(10, c1.get() + c2.get()); + + cc1.stop(); + cc2.stop(); + + // Delete static consumer group + StaticConsumerGroup.delete(nc, streamName, cgName); + nc.close(); + } + } + + /** + * Ported from Go TestElastic. + * Creates a stream, tests elastic consume with member add/delete. + * Verifies partition redistribution when members are added and removed. + */ + @Test + void testElastic() throws Exception { + try (NatsServerRunner server = NatsServerRunner.builder().jetstream(true).build()) { + Connection nc = Nats.connect(server.getNatsLocalhostUri()); + + String streamName = "test"; + String cgName = "group"; + AtomicInteger c1 = new AtomicInteger(0); + AtomicInteger c2 = new AtomicInteger(0); + + // Create a stream (no subject transform needed for elastic) + nc.jetStreamManagement().addStream(StreamConfiguration.builder() + .name(streamName) + .subjects("bar.*") + .storageType(StorageType.Memory) + .build()); + + // Publish 10 messages + for (int i = 0; i < 10; i++) { + nc.jetStream().publish("bar." + i, "payload".getBytes()); + } + + ConsumerConfiguration config = ConsumerConfiguration.builder() + .maxAckPending(1) + .ackWait(Duration.ofSeconds(1)) + .build(); + + // Create elastic consumer group + ElasticConsumerGroup.create(nc, streamName, cgName, 2, "bar.*", + new int[]{1}, -1, -1); + + // Start consuming on both members + ConsumerGroupConsumeContext cc1 = ElasticConsumerGroup.consume(nc, streamName, cgName, "m1", msg -> { + c1.incrementAndGet(); + try { + msg.ack(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, config); + + ConsumerGroupConsumeContext cc2 = ElasticConsumerGroup.consume(nc, streamName, cgName, "m2", msg -> { + c2.incrementAndGet(); + try { + msg.ack(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, config); + + // Wait for KV watchers to be established on the instance threads + Thread.sleep(200); + + // Add only m1 first + ElasticConsumerGroup.addMembers(nc, streamName, cgName, Collections.singletonList("m1")); + + // Wait for m1 to consume all 10 messages (only m1 is a member) + long deadline = System.currentTimeMillis() + 5000; + while (c1.get() != 10 || c2.get() != 0) { + Thread.sleep(100); + if (System.currentTimeMillis() > deadline) { + fail("timeout phase 1: c1=" + c1.get() + " c2=" + c2.get() + " expected c1=10 c2=0"); + } + } + assertEquals(10, c1.get()); + assertEquals(0, c2.get()); + + // Add m2 + ElasticConsumerGroup.addMembers(nc, streamName, cgName, Collections.singletonList("m2")); + + // Wait for consumers to be recreated + Thread.sleep(50); + + // Publish 10 more messages + for (int i = 0; i < 10; i++) { + nc.jetStream().publish("bar." + i, "payload".getBytes()); + } + + // Wait for split consumption (m1=15, m2=5) + deadline = System.currentTimeMillis() + 10000; + while (c1.get() != 15 || c2.get() != 5) { + Thread.sleep(100); + if (System.currentTimeMillis() > deadline) { + fail("timeout phase 2: c1=" + c1.get() + " c2=" + c2.get() + " expected c1=15 c2=5"); + } + } + + // Delete m1 + ElasticConsumerGroup.deleteMembers(nc, streamName, cgName, Collections.singletonList("m1")); + + // Wait for consumers to be recreated + Thread.sleep(50); + + // Publish 10 more messages + for (int i = 0; i < 10; i++) { + nc.jetStream().publish("bar." + i, "payload".getBytes()); + } + + // Wait for m2 to consume all (m1=15 stays, m2=15) + deadline = System.currentTimeMillis() + 10000; + while (c1.get() != 15 || c2.get() != 15) { + Thread.sleep(100); + if (System.currentTimeMillis() > deadline) { + fail("timeout phase 3: c1=" + c1.get() + " c2=" + c2.get() + " expected c1=15 c2=15"); + } + } + + cc1.stop(); + cc2.stop(); + + // Delete elastic consumer group + ElasticConsumerGroup.delete(nc, streamName, cgName); + nc.close(); + } + } +} diff --git a/pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java b/pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java new file mode 100644 index 0000000..fa65bc3 --- /dev/null +++ b/pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java @@ -0,0 +1,272 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for PartitionUtils. + * These tests verify the partition distribution algorithm matches the Go implementation. + */ +class PartitionUtilsTest { + + @Test + void testComposeKey() { + assertEquals("mystream.mycg", PartitionUtils.composeKey("mystream", "mycg")); + assertEquals("stream1.cg2", PartitionUtils.composeKey("stream1", "cg2")); + } + + @Test + void testComposeCGSName() { + assertEquals("mystream-mycg", PartitionUtils.composeCGSName("mystream", "mycg")); + assertEquals("stream1-cg2", PartitionUtils.composeCGSName("stream1", "cg2")); + } + + @Test + void testComposeStaticConsumerName() { + assertEquals("mycg-member1", PartitionUtils.composeStaticConsumerName("mycg", "member1")); + assertEquals("cg-m2", PartitionUtils.composeStaticConsumerName("cg", "m2")); + } + + @Test + void testGeneratePartitionFiltersBalanced() { + // Test with 6 partitions and 3 members - should distribute evenly + List members = Arrays.asList("m1", "m2", "m3"); + int maxMembers = 6; + + List m1Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m1"); + List m2Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m2"); + List m3Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m3"); + + // Each member should get 2 partitions + assertEquals(2, m1Filters.size()); + assertEquals(2, m2Filters.size()); + assertEquals(2, m3Filters.size()); + + // m1 gets partitions 0, 1 + assertTrue(m1Filters.contains("0.>")); + assertTrue(m1Filters.contains("1.>")); + + // m2 gets partitions 2, 3 + assertTrue(m2Filters.contains("2.>")); + assertTrue(m2Filters.contains("3.>")); + + // m3 gets partitions 4, 5 + assertTrue(m3Filters.contains("4.>")); + assertTrue(m3Filters.contains("5.>")); + } + + @Test + void testGeneratePartitionFiltersUneven() { + // Test with 7 partitions and 3 members - remainder goes to first member + List members = Arrays.asList("m1", "m2", "m3"); + int maxMembers = 7; + + List m1Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m1"); + List m2Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m2"); + List m3Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m3"); + + // m1 gets 3 partitions (0, 1, 6) + assertEquals(3, m1Filters.size()); + assertTrue(m1Filters.contains("0.>")); + assertTrue(m1Filters.contains("1.>")); + assertTrue(m1Filters.contains("6.>")); + + // m2 gets 2 partitions (2, 3) + assertEquals(2, m2Filters.size()); + assertTrue(m2Filters.contains("2.>")); + assertTrue(m2Filters.contains("3.>")); + + // m3 gets 2 partitions (4, 5) + assertEquals(2, m3Filters.size()); + assertTrue(m3Filters.contains("4.>")); + assertTrue(m3Filters.contains("5.>")); + } + + @Test + void testGeneratePartitionFiltersMemberMappings() { + // Test with explicit member mappings + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 2, 4}), + new MemberMapping("bob", new int[]{1, 3, 5}) + ); + + List aliceFilters = PartitionUtils.generatePartitionFilters(null, 6, mappings, "alice"); + List bobFilters = PartitionUtils.generatePartitionFilters(null, 6, mappings, "bob"); + List charlieFilters = PartitionUtils.generatePartitionFilters(null, 6, mappings, "charlie"); + + assertEquals(3, aliceFilters.size()); + assertTrue(aliceFilters.contains("0.>")); + assertTrue(aliceFilters.contains("2.>")); + assertTrue(aliceFilters.contains("4.>")); + + assertEquals(3, bobFilters.size()); + assertTrue(bobFilters.contains("1.>")); + assertTrue(bobFilters.contains("3.>")); + assertTrue(bobFilters.contains("5.>")); + + // Non-member gets no partitions + assertTrue(charlieFilters.isEmpty()); + } + + @Test + void testGeneratePartitionFiltersDeduplicates() { + // Test that duplicate members are deduplicated + List members = Arrays.asList("m1", "m2", "m1", "m2", "m3"); + int maxMembers = 6; + + List m1Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m1"); + List m2Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m2"); + List m3Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m3"); + + // After deduplication, should be same as 3 unique members + assertEquals(2, m1Filters.size()); + assertEquals(2, m2Filters.size()); + assertEquals(2, m3Filters.size()); + } + + @Test + void testGeneratePartitionFiltersSortsMembers() { + // Test that members are sorted before distribution + // Even if passed in different order, same member should get same partitions + List members1 = Arrays.asList("m3", "m1", "m2"); + List members2 = Arrays.asList("m1", "m2", "m3"); + int maxMembers = 6; + + List m1Filters1 = PartitionUtils.generatePartitionFilters(members1, maxMembers, null, "m1"); + List m1Filters2 = PartitionUtils.generatePartitionFilters(members2, maxMembers, null, "m1"); + + // Should get same partitions regardless of input order + assertEquals(m1Filters1, m1Filters2); + } + + @Test + void testGeneratePartitionFiltersCapToMaxMembers() { + // Test that members are capped to maxMembers + List members = Arrays.asList("m1", "m2", "m3", "m4", "m5"); + int maxMembers = 3; + + // After sorting: m1, m2, m3, m4, m5 + // Capped to first 3: m1, m2, m3 + // m4 and m5 should not get any partitions + + List m1Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m1"); + List m4Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m4"); + List m5Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m5"); + + assertEquals(1, m1Filters.size()); + assertTrue(m4Filters.isEmpty()); + assertTrue(m5Filters.isEmpty()); + } + + @Test + void testGeneratePartitionFiltersEmptyMembers() { + List filters = PartitionUtils.generatePartitionFilters(Collections.emptyList(), 6, null, "m1"); + assertTrue(filters.isEmpty()); + } + + @Test + void testGeneratePartitionFiltersNullMembers() { + List filters = PartitionUtils.generatePartitionFilters(null, 6, null, "m1"); + assertTrue(filters.isEmpty()); + } + + @Test + void testGeneratePartitionFiltersSingleMember() { + List members = Collections.singletonList("m1"); + int maxMembers = 4; + + List m1Filters = PartitionUtils.generatePartitionFilters(members, maxMembers, null, "m1"); + + // Single member should get all partitions + assertEquals(4, m1Filters.size()); + assertTrue(m1Filters.contains("0.>")); + assertTrue(m1Filters.contains("1.>")); + assertTrue(m1Filters.contains("2.>")); + assertTrue(m1Filters.contains("3.>")); + } + + @Test + void testDeduplicateStringList() { + List input = Arrays.asList("a", "b", "a", "c", "b"); + List result = PartitionUtils.deduplicateStringList(input); + + assertEquals(3, result.size()); + assertEquals("a", result.get(0)); + assertEquals("b", result.get(1)); + assertEquals("c", result.get(2)); + } + + @Test + void testDeduplicateStringListNull() { + List result = PartitionUtils.deduplicateStringList(null); + assertTrue(result.isEmpty()); + } + + @Test + void testCalculatePullExpiry() { + // 10 second ack wait -> 5 second pull expiry + Duration ackWait = Duration.ofSeconds(10); + Duration pullExpiry = PartitionUtils.calculatePullExpiry(ackWait); + assertEquals(Duration.ofSeconds(5), pullExpiry); + + // 1 second ack wait -> 1 second pull expiry (min is 1 second) + ackWait = Duration.ofSeconds(1); + pullExpiry = PartitionUtils.calculatePullExpiry(ackWait); + assertEquals(Duration.ofSeconds(1), pullExpiry); + + // 500ms ack wait -> 1 second pull expiry (min is 1 second) + ackWait = Duration.ofMillis(500); + pullExpiry = PartitionUtils.calculatePullExpiry(ackWait); + assertEquals(Duration.ofSeconds(1), pullExpiry); + } + + @Test + void testCalculatePinnedTTL() { + // 10 second ack wait -> 10 second pinned TTL + Duration ackWait = Duration.ofSeconds(10); + Duration pinnedTTL = PartitionUtils.calculatePinnedTTL(ackWait); + assertEquals(Duration.ofSeconds(10), pinnedTTL); + + // 500ms ack wait -> 1 second pinned TTL (min is 1 second) + ackWait = Duration.ofMillis(500); + pinnedTTL = PartitionUtils.calculatePinnedTTL(ackWait); + assertEquals(Duration.ofSeconds(1), pinnedTTL); + } + + @Test + void testCalculateInactiveThreshold() { + Duration ackWait = Duration.ofSeconds(5); + Duration threshold = PartitionUtils.calculateInactiveThreshold(ackWait); + assertEquals(Duration.ofSeconds(5), threshold); + } + + @Test + void testConstants() { + assertEquals(2, PartitionUtils.PULL_TIMEOUT_DIVIDER); + assertEquals(1, PartitionUtils.CONSUMER_IDLE_TIMEOUT_FACTOR); + assertEquals(Duration.ofSeconds(5), PartitionUtils.DEFAULT_ACK_WAIT); + assertEquals(Duration.ofSeconds(1), PartitionUtils.MIN_PULL_EXPIRY_PINNED_TTL); + assertEquals("PCG", PartitionUtils.PRIORITY_GROUP_NAME); + assertEquals("static-consumer-groups", PartitionUtils.KV_STATIC_BUCKET_NAME); + assertEquals("elastic-consumer-groups", PartitionUtils.KV_ELASTIC_BUCKET_NAME); + } +} diff --git a/pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java b/pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java new file mode 100644 index 0000000..dd8baeb --- /dev/null +++ b/pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java @@ -0,0 +1,305 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import io.synadia.pcg.exceptions.ConsumerGroupException; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for StaticConsumerGroupConfig. + * Tests config validation and JSON serialization compatibility with Go. + */ +class StaticConsumerGroupTest { + + private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + + @Test + void testConfigWithMembers() { + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m2", "m3", "m4"), + new ArrayList<>() + ); + + assertEquals(4, config.getMaxMembers()); + assertEquals("foo.>", config.getFilter()); + assertEquals(4, config.getMembers().size()); + assertTrue(config.getMemberMappings().isEmpty()); + + assertTrue(config.isInMembership("m1")); + assertTrue(config.isInMembership("m4")); + assertFalse(config.isInMembership("m5")); + } + + @Test + void testConfigWithMemberMappings() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2, 3}) + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", new ArrayList<>(), mappings + ); + + assertEquals(4, config.getMaxMembers()); + assertTrue(config.getMembers().isEmpty()); + assertEquals(2, config.getMemberMappings().size()); + + assertTrue(config.isInMembership("alice")); + assertTrue(config.isInMembership("bob")); + assertFalse(config.isInMembership("charlie")); + } + + @Test + void testValidationMaxMembersZero() { + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 0, "foo.>", + Arrays.asList("m1", "m2"), + new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("max number of members must be >= 1")); + } + + @Test + void testValidationBothMembersAndMappings() { + List mappings = Collections.singletonList( + new MemberMapping("alice", new int[]{0, 1}) + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 2, "foo.>", + Arrays.asList("m1", "m2"), + mappings + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("either members or member mappings must be provided, not both")); + } + + @Test + void testValidationDuplicateMemberNames() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("alice", new int[]{2, 3}) + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", new ArrayList<>(), mappings + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("member names must be unique")); + } + + @Test + void testValidationDuplicatePartitions() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{1, 2}) + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 3, "foo.>", new ArrayList<>(), mappings + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("partition numbers must be used only once")); + } + + @Test + void testValidationPartitionOutOfRange() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2, 5}) // 5 is out of range for maxMembers=4 + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", new ArrayList<>(), mappings + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("partition numbers must be between 0 and one less")); + } + + @Test + void testValidationNotAllPartitionsCovered() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2}) // Missing partition 3 + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", new ArrayList<>(), mappings + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("number of unique partition numbers must be equal")); + } + + @Test + void testValidationSuccess() throws ConsumerGroupException { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2, 3}) + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", new ArrayList<>(), mappings + ); + + assertDoesNotThrow(config::validate); + } + + @Test + void testValidationWithMembersSuccess() throws ConsumerGroupException { + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m2", "m3", "m4"), + new ArrayList<>() + ); + + assertDoesNotThrow(config::validate); + } + + @Test + void testJsonSerializationWithMembers() { + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m2"), + new ArrayList<>() + ); + + String json = GSON.toJson(config); + + // Verify JSON structure matches Go format + assertTrue(json.contains("\"max_members\":4")); + assertTrue(json.contains("\"filter\":\"foo.>\"")); + assertTrue(json.contains("\"members\":[\"m1\",\"m2\"]")); + + // Deserialize and verify + StaticConsumerGroupConfig deserialized = GSON.fromJson(json, StaticConsumerGroupConfig.class); + assertEquals(config.getMaxMembers(), deserialized.getMaxMembers()); + assertEquals(config.getFilter(), deserialized.getFilter()); + assertEquals(config.getMembers(), deserialized.getMembers()); + } + + @Test + void testJsonSerializationWithMappings() { + List mappings = Arrays.asList( + new MemberMapping("alice", new int[]{0, 1}), + new MemberMapping("bob", new int[]{2, 3}) + ); + + StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( + 4, "foo.>", new ArrayList<>(), mappings + ); + + String json = GSON.toJson(config); + + // Verify JSON structure matches Go format + assertTrue(json.contains("\"max_members\":4")); + assertTrue(json.contains("\"member_mappings\"")); + assertTrue(json.contains("\"member\":\"alice\"")); + assertTrue(json.contains("\"partitions\":[0,1]")); + + // Deserialize and verify + StaticConsumerGroupConfig deserialized = GSON.fromJson(json, StaticConsumerGroupConfig.class); + assertEquals(config.getMaxMembers(), deserialized.getMaxMembers()); + assertEquals(2, deserialized.getMemberMappings().size()); + assertEquals("alice", deserialized.getMemberMappings().get(0).getMember()); + assertArrayEquals(new int[]{0, 1}, deserialized.getMemberMappings().get(0).getPartitions()); + } + + @Test + void testJsonDeserializationFromGo() { + // This JSON is in the format produced by the Go implementation + String goJson = "{\"max_members\":4,\"filter\":\"foo.>\",\"members\":[\"m1\",\"m2\",\"m3\",\"m4\"]}"; + + StaticConsumerGroupConfig config = GSON.fromJson(goJson, StaticConsumerGroupConfig.class); + + assertEquals(4, config.getMaxMembers()); + assertEquals("foo.>", config.getFilter()); + assertEquals(4, config.getMembers().size()); + assertEquals("m1", config.getMembers().get(0)); + } + + @Test + void testJsonDeserializationWithMappingsFromGo() { + // This JSON is in the format produced by the Go implementation + String goJson = "{\"max_members\":4,\"filter\":\"bar.>\",\"member_mappings\":[{\"member\":\"alice\",\"partitions\":[0,1]},{\"member\":\"bob\",\"partitions\":[2,3]}]}"; + + StaticConsumerGroupConfig config = GSON.fromJson(goJson, StaticConsumerGroupConfig.class); + + assertEquals(4, config.getMaxMembers()); + assertEquals("bar.>", config.getFilter()); + assertEquals(2, config.getMemberMappings().size()); + assertEquals("alice", config.getMemberMappings().get(0).getMember()); + assertArrayEquals(new int[]{0, 1}, config.getMemberMappings().get(0).getPartitions()); + assertEquals("bob", config.getMemberMappings().get(1).getMember()); + assertArrayEquals(new int[]{2, 3}, config.getMemberMappings().get(1).getPartitions()); + } + + @Test + void testEquals() { + StaticConsumerGroupConfig config1 = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m2"), + new ArrayList<>() + ); + + StaticConsumerGroupConfig config2 = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m2"), + new ArrayList<>() + ); + + StaticConsumerGroupConfig config3 = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m3"), + new ArrayList<>() + ); + + assertEquals(config1, config2); + assertNotEquals(config1, config3); + } + + @Test + void testHashCode() { + StaticConsumerGroupConfig config1 = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m2"), + new ArrayList<>() + ); + + StaticConsumerGroupConfig config2 = new StaticConsumerGroupConfig( + 4, "foo.>", + Arrays.asList("m1", "m2"), + new ArrayList<>() + ); + + assertEquals(config1.hashCode(), config2.hashCode()); + } +} From b9024bb0d853af848eb2cf50b44cfd09e4700c4d Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 19 Feb 2026 20:09:34 -0500 Subject: [PATCH 091/135] Schedule Stuff --- batch-publish/build.gradle | 4 +- build-tester/build.gradle | 63 ++-- chaos-runner/build.gradle | 2 +- counters/build.gradle | 4 +- direct-batch/build.gradle | 2 +- encoded-kv/build.gradle | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- schedule-message/build.gradle | 132 ++++---- .../gradle/wrapper/gradle-wrapper.jar | Bin 58694 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- schedule-message/gradlew | 286 +++++++++++------- schedule-message/gradlew.bat | 59 ++-- schedule-message/settings.gradle | 21 +- .../io/synadia/examples/ScheduleBasics.java | 16 +- .../synadia/sm/ScheduledMessageBuilder.java | 85 ++++++ utils/build.gradle | 2 +- 18 files changed, 413 insertions(+), 275 deletions(-) diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index 1a7791d..64b8a10 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -39,7 +39,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' @@ -47,7 +47,7 @@ dependencies { testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.3' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } sourceSets { diff --git a/build-tester/build.gradle b/build-tester/build.gradle index 83313f0..323a6cc 100644 --- a/build-tester/build.gradle +++ b/build-tester/build.gradle @@ -5,14 +5,13 @@ plugins { id("java-library") id("maven-publish") id("jacoco") -// id("biz.aQute.bnd.builder") version "7.1.0" - id("biz.aQute.bnd.builder") version "5.1.2" + id("biz.aQute.bnd.builder") version "7.1.0" id("org.gradle.test-retry") version "1.6.4" - id("io.github.gradle-nexus.publish-plugin") version "1.1.0" + id("io.github.gradle-nexus.publish-plugin") version "2.0.0" id("signing") } -def jarVersion = "0.0.4" +def jarVersion = "0.0.7" group = 'io.synadia' def isRelease = System.getenv("BUILD_EVENT") == "release" @@ -29,26 +28,19 @@ java { targetCompatibility = targetCompat } -// java { -// sourceCompatibility = JavaVersion.VERSION_17 -// targetCompatibility = JavaVersion.VERSION_17 -// } - repositories { - gradlePluginPortal() mavenCentral() maven { url="https://repo1.maven.org/maven2/" } - maven { url="https://central.sonatype.com/repository/maven-snapshots/" } - maven { url="https://oss.sonatype.org/content/repositories/releases/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots" } } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' implementation 'org.jspecify:jspecify:1.0.0' - implementation 'org.apache.commons:commons-lang3:3.18.0' + implementation 'io.synadia:counters:0.2.1-SNAPSHOT' - testImplementation 'io.nats:jnats-server-runner:1.2.8' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' + testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } sourceSets { @@ -69,23 +61,17 @@ sourceSets { tasks.register('bundle', Bundle) { from sourceSets.main.output - exclude("io/synadia/examples/**") + exclude("io/synadia/bt/examples/**") } jar { - manifest { - attributes( - 'Automatic-Module-Name': 'io.synadia.build.tester', - 'Implementation-Title': 'Build Tester', - 'Implementation-Version': jarVersion, - 'Implementation-Vendor': 'synadia.io' + bundle { + bnd("Bundle-Name": "io.synadia.build.tester", + "Bundle-Vendor": "synadia.io", + "Bundle-Description": "Build Tester", + "Bundle-DocURL": "https://synadia.io" ) } - bnd (['Implementation-Title': 'Java Nats', - 'Implementation-Version': jarVersion, - 'Implementation-Vendor': 'nats.io'] - ) - exclude("io/synadia/examples/**") } test { @@ -120,7 +106,7 @@ tasks.register('examplesJar', Jar) { 'Implementation-Vendor': 'synadia.io') } from(sourceSets.main.output) { - include "io/synadia/flink/examples/**" + include "io/synadia/bt/examples/**" } } @@ -180,11 +166,12 @@ publishing { artifact javadocJar artifact testsJar pom { - name = 'build-tester' - packaging = 'jar' + name = jarAndArtifactName + packaging = "jar" groupId = group - artifactId = archivesBaseName - description = 'Synadia Communications Inc. Build Tester' + artifactId = jarAndArtifactName + description = "Synadia Communications Inc. Build Tester" + url = "https://github.com/synadia-io/orbit.java/tree/main/build-tester" licenses { license { name = 'The Apache License, Version 2.0' @@ -199,6 +186,9 @@ publishing { url = "https://synadia.io" } } + scm { + url = "https://github.com/synadia-io/orbit.java" + } } } } @@ -214,10 +204,3 @@ if (isRelease) { sign publishing.publications.mavenJava } } - -if (hasProperty('buildScan')) { - buildScan { - termsOfServiceUrl = 'https://gradle.com/help/legal-terms-of-use' - termsOfServiceAgree = 'yes' - } -} \ No newline at end of file diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 5ba45fd..1719c8e 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' // this is only for the example and the uber jar won't include it - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/counters/build.gradle b/counters/build.gradle index 22b11e4..996c923 100644 --- a/counters/build.gradle +++ b/counters/build.gradle @@ -39,7 +39,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' implementation 'io.synadia:direct-batch:0.1.4' implementation 'org.jspecify:jspecify:1.0.0' @@ -49,7 +49,7 @@ dependencies { testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.3' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } sourceSets { diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 994ebf4..5602936 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index 49c9975..a508fd4 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index 6ff9cbf..f194363 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index febb858..f3c0597 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index d023c4c..fe663de 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 3d242b7..b249c47 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -1,55 +1,46 @@ import aQute.bnd.gradle.Bundle -import org.gradle.internal.os.OperatingSystem plugins { - id 'java' - id 'java-library' - id 'maven-publish' - id 'jacoco' - id 'com.github.kt3k.coveralls' version '2.12.0' - id 'biz.aQute.bnd.builder' version '5.1.2' - id "org.gradle.test-retry" version "1.1.9" - id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' - id 'signing' + id("java") + id("java-library") + id("maven-publish") + id("jacoco") + id("biz.aQute.bnd.builder") version "7.1.0" + id("org.gradle.test-retry") version "1.6.4" + id("io.github.gradle-nexus.publish-plugin") version "2.0.0" + id("signing") } def jarVersion = "0.0.3" group = 'io.synadia' -def isMerge = System.getenv("BUILD_EVENT") == "push" def isRelease = System.getenv("BUILD_EVENT") == "release" -// version is the variable the build actually uses. -version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" +def tc = System.getenv("TARGET_COMPATIBILITY"); +def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) +def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") +def jarAndArtifactName = "schedule-message" + jarEnd + +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. java { sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = targetCompat } repositories { mavenCentral() - maven { - url "https://repo1.maven.org/maven2/" - } - maven { - url "https://central.sonatype.com/repository/maven-snapshots/" - } + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots" } } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' implementation 'org.jspecify:jspecify:1.0.0' + implementation 'io.synadia:counters:0.2.1-SNAPSHOT' - testImplementation 'commons-codec:commons-codec:1.18.0' - testImplementation 'io.nats:jnats-server-runner:1.2.8' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' - testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' - testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' -} - -configurations.configureEach { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' + testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } sourceSets { @@ -57,6 +48,9 @@ sourceSets { java { srcDirs = ['src/main/java','src/examples/java'] } + resources { + srcDirs = ['src/examples/resources'] + } } test { java { @@ -67,45 +61,41 @@ sourceSets { tasks.register('bundle', Bundle) { from sourceSets.main.output - exclude("io/synadia/examples/**") + exclude("io/synadia/bt/examples/**") } jar { - manifest { - attributes('Automatic-Module-Name': 'io.synadia.schedule.message') + bundle { + bnd("Bundle-Name": "io.synadia.schedule.message", + "Bundle-Vendor": "synadia.io", + "Bundle-Description": "JetStream Scheduled Messages", + "Bundle-DocURL": "https://synadia.io" + ) } - bnd (['Implementation-Title': 'JetStream Scheduled Messages', - 'Implementation-Version': jarVersion, - 'Implementation-Vendor': 'synadia.io'] - ) - exclude("io/synadia/examples/**") } test { // Use junit platform for unit tests useJUnitPlatform() + testLogging { + exceptionFormat = 'full' + events "started", "passed", "skipped", "failed" + showStandardStreams = true + } + retry { + failOnPassedAfterRetry = false + maxFailures = 3 + maxRetries = 3 + } + systemProperty 'junit.jupiter.execution.timeout.default', '3m' } javadoc { options.overview = 'src/main/javadoc/overview.html' // relative to source root source = sourceSets.main.allJava title = "Synadia Communications Inc. JetStream Scheduled Messages" + excludes ['**/examples/**'] classpath = sourceSets.main.runtimeClasspath - doLast { - if (!OperatingSystem.current().isWindows()) { - exec { - println "Updating favicon on all html files" - workingDir 'build/docs/javadoc' - // Only on linux, mac at this point - commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' - } - copy { - println "Copying images to javadoc folder" - from 'src/main/javadoc/images' - into 'build/docs/javadoc' - } - } - } } tasks.register('examplesJar', Jar) { @@ -116,7 +106,7 @@ tasks.register('examplesJar', Jar) { 'Implementation-Vendor': 'synadia.io') } from(sourceSets.main.output) { - include "io/synadia/examples/**" + include "io/synadia/bt/examples/**" } } @@ -130,27 +120,32 @@ tasks.register('sourcesJar', Jar) { from sourceSets.main.allSource } +tasks.register('testsJar', Jar) { + archiveClassifier.set('tests') + from sourceSets.test.allSource +} + +artifacts { + archives javadocJar, sourcesJar, examplesJar, testsJar +} + jacoco { - toolVersion = "0.8.6" + toolVersion = "0.8.12" } jacocoTestReport { reports { - xml.enabled = true // coveralls plugin depends on xml format report - html.enabled = true + xml.required = true // coveralls plugin depends on xml format report + html.required = true } afterEvaluate { // only report on main library not examples classDirectories.setFrom(files(classDirectories.files.collect { fileTree(dir: it, - exclude: ['**/examples**']) + exclude: ['**/examples**','**/Debug**']) })) } } -artifacts { - archives javadocJar, sourcesJar, examplesJar -} - nexusPublishing { repositories { sonatype { @@ -169,13 +164,14 @@ publishing { artifact sourcesJar artifact examplesJar artifact javadocJar + artifact testsJar pom { - name = 'schedule-message' - packaging = 'jar' + name = jarAndArtifactName + packaging = "jar" groupId = group - artifactId = archivesBaseName - description = 'Synadia Communications Inc. JetStream Scheduled Messages' - url = 'https://github.com/synadia-io/orbit.java' + artifactId = jarAndArtifactName + description = "Synadia Communications Inc. JetStream Scheduled Messages" + url = "https://github.com/synadia-io/orbit.java/tree/main/schedule-message" licenses { license { name = 'The Apache License, Version 2.0' @@ -191,7 +187,7 @@ publishing { } } scm { - url = 'https://github.com/synadia-io/orbit.java' + url = "https://github.com/synadia-io/orbit.java" } } } diff --git a/schedule-message/gradle/wrapper/gradle-wrapper.jar b/schedule-message/gradle/wrapper/gradle-wrapper.jar index 490fda8577df6c95960ba7077c43220e5bb2c0d9..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch literal 43764 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 diff --git a/schedule-message/gradle/wrapper/gradle-wrapper.properties b/schedule-message/gradle/wrapper/gradle-wrapper.properties index 442d913..ca025c8 100644 --- a/schedule-message/gradle/wrapper/gradle-wrapper.properties +++ b/schedule-message/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/schedule-message/gradlew b/schedule-message/gradlew index 2fe81a7..23d15a9 100644 --- a/schedule-message/gradlew +++ b/schedule-message/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,80 +15,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,87 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/schedule-message/gradlew.bat b/schedule-message/gradlew.bat index 62bd9b9..db3a6ac 100644 --- a/schedule-message/gradlew.bat +++ b/schedule-message/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -54,48 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/schedule-message/settings.gradle b/schedule-message/settings.gradle index df380d0..d37f9a3 100644 --- a/schedule-message/settings.gradle +++ b/schedule-message/settings.gradle @@ -1,10 +1,13 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html - */ - +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://plugins.gradle.org/m2/" } + } + plugins { + id("biz.aQute.bnd.builder") version "7.1.0" + } +} rootProject.name = 'schedule-message' diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java index 5138df3..fb26069 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java @@ -11,6 +11,7 @@ import io.synadia.sm.ScheduledStreamUtil; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static io.synadia.examples.ScheduleExampleUtils.report; @@ -43,18 +44,18 @@ public static void main(String[] args) { StreamInfo si = ScheduledStreamUtil.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); report("Created stream", si.getConfiguration()); - CountDownLatch latch = new CountDownLatch(2); + CountDownLatch latch = new CountDownLatch(4); Dispatcher d = connection.createDispatcher(); // subscribe to the subject that receives the schedule message js.subscribe(SCHEDULES, d, m -> { - report("SCHEDULE", m); + report("SCHEDULED", m); m.ack(); }, false); // subscribe to the target subject js.subscribe(TARGETS, d, m -> { - report("TARGET", m); + report("RECEIVED", m); m.ack(); latch.countDown(); }, false); @@ -77,6 +78,15 @@ public static void main(String[] args) { report("PUBLISH", m); js.publish(m); + m = new ScheduledMessageBuilder() + .scheduleSubject(SCHEDULE_PREFIX + "at") + .targetSubject(TARGET_PREFIX + "at") + .scheduleEvery(1, TimeUnit.SECONDS) + .data("Every Second") + .build(); + report("PUBLISH", m); + js.publish(m); + latch.await(); } } diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index c16804f..6401be2 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -10,16 +10,20 @@ import io.nats.client.support.DateTimeUtils; import io.nats.client.support.NatsJetStreamConstants; import io.nats.client.support.Validator; +import org.jspecify.annotations.NonNull; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.time.ZonedDateTime; +import java.util.concurrent.TimeUnit; /** * Class to make a message that can be published to a stream that allows message scheduling */ public class ScheduledMessageBuilder { + public static final long NANOS_PER_SECOND = 1_000_000_000L; private String scheduleString; private String scheduleSubject; private String targetSubject; @@ -149,6 +153,34 @@ public ScheduledMessageBuilder scheduleEvery(String every) { return this; } + /** + * Schedule an interval + * @param every a duration, validated to be at least 1 second + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleEvery(Duration every) { + if (every == null) { + scheduleString = null; + } + else { + if (every.toNanos() < NANOS_PER_SECOND) { + throw new IllegalArgumentException("Expiry cannot be less than 1 second."); + } + scheduleString = "@every " + toGoDuration(every); + } + return this; + } + + /** + * Schedule an interval + * @param duration a duration, validated to be at least 1 second + * @param timeUnit the unit for the duration + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleEvery(int duration, TimeUnit timeUnit) { + return scheduleEvery(Duration.ofNanos(timeUnit.toNanos(duration))); + } + /** * Schedule based on standard cron * @param cron A valid cron string @@ -190,4 +222,57 @@ public Message build() { .data(data) .build(); } + + public static String toGoDuration(Duration duration) { + long left = duration.toNanos(); + long nanos = left % 1_000_000L; + left = (left - nanos) / 1_000_000L; + long millis = left % 1_000L; + left = (left - millis) / 1_000L; + long seconds = left % 60L; + left = (left - seconds) / 60L; + long minutes = left % 60L; + long hours = (left - minutes) / 60L; + + StringBuilder sb = new StringBuilder(); + if (hours > 0) sb.append(hours).append('h'); + if (minutes > 0) sb.append(minutes).append('m'); + if (seconds > 0) sb.append(seconds).append('s'); + if (millis > 0) sb.append(millis).append("ms"); + if (nanos > 0) sb.append(nanos).append("ns"); + + return sb.toString(); + } + + public static boolean isAtLeastOneSecond(@NonNull String s) { + long totalNanos = 0; + int i = 0; + + try { + while (i < s.length()) { + int start = i; + while (i < s.length() && Character.isDigit(s.charAt(i))) i++; + if (i == start) return false; + long value = Long.parseLong(s.substring(start, i)); + + int unitStart = i; + while (i < s.length() && Character.isLetter(s.charAt(i))) i++; + String unit = s.substring(unitStart, i); + + switch (unit) { + case "h": totalNanos += value * 3_600_000_000_000L; break; + case "m": totalNanos += value * 60_000_000_000L; break; + case "s": totalNanos += value * NANOS_PER_SECOND; break; + case "ms": totalNanos += value * 1_000_000L; break; + case "us": totalNanos += value * 1_000L; break; + case "ns": totalNanos += value; break; + default: return false; + } + } + } catch (Exception e) { + return false; + } + + return totalNanos >= NANOS_PER_SECOND; + } } diff --git a/utils/build.gradle b/utils/build.gradle index 406a587..ce382a0 100644 --- a/utils/build.gradle +++ b/utils/build.gradle @@ -24,5 +24,5 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.24.0' + implementation 'io.nats:jnats:2.25.1' } From 671e632aa150a2b848fb3e6e7eeff01e2809bc33 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 19 Feb 2026 20:42:54 -0500 Subject: [PATCH 092/135] PCG Build Prep --- .github/workflows/pcg-main.yml | 47 ++++ .github/workflows/pcg-pr.yml | 41 +++ .github/workflows/pcg-release.yml | 39 +++ pcgroups/build.gradle | 188 +++++++++++++ pcgroups/gradle/libs.versions.toml | 12 + pcgroups/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + pcgroups/gradlew | 251 ++++++++++++++++++ pcgroups/gradlew.bat | 94 +++++++ pcgroups/settings.gradle | 13 + 10 files changed, 692 insertions(+) create mode 100644 .github/workflows/pcg-main.yml create mode 100644 .github/workflows/pcg-pr.yml create mode 100644 .github/workflows/pcg-release.yml create mode 100644 pcgroups/build.gradle create mode 100644 pcgroups/gradle/libs.versions.toml create mode 100644 pcgroups/gradle/wrapper/gradle-wrapper.jar create mode 100644 pcgroups/gradle/wrapper/gradle-wrapper.properties create mode 100644 pcgroups/gradlew create mode 100644 pcgroups/gradlew.bat create mode 100644 pcgroups/settings.gradle diff --git a/.github/workflows/pcg-main.yml b/.github/workflows/pcg-main.yml new file mode 100644 index 0000000..4283082 --- /dev/null +++ b/.github/workflows/pcg-main.yml @@ -0,0 +1,47 @@ +name: Partitioned Consumer Groups Main Snapshot + +on: + push: + branches: + - main + paths: + - 'pcgroups/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd pcgroups + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd pcgroups + ./gradlew javadoc + popd + - name: Publish Snapshot + run: | + pushd pcgroups + ./gradlew -i publishToSonatype + popd diff --git a/.github/workflows/pcg-pr.yml b/.github/workflows/pcg-pr.yml new file mode 100644 index 0000000..a667974 --- /dev/null +++ b/.github/workflows/pcg-pr.yml @@ -0,0 +1,41 @@ +name: Partitioned Consumer Groups Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'pcgroups/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd pcgroups + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd pcgroups + ./gradlew javadoc + popd diff --git a/.github/workflows/pcg-release.yml b/.github/workflows/pcg-release.yml new file mode 100644 index 0000000..7406349 --- /dev/null +++ b/.github/workflows/pcg-release.yml @@ -0,0 +1,39 @@ +name: Partitioned Consumer Groups Release + +on: + push: + tags: [ 'pcg/*' ] + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: "release" + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd pcgroups + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify, Sign and Publish Release + run: | + pushd pcgroups + ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + popd diff --git a/pcgroups/build.gradle b/pcgroups/build.gradle new file mode 100644 index 0000000..383c6fe --- /dev/null +++ b/pcgroups/build.gradle @@ -0,0 +1,188 @@ +import aQute.bnd.gradle.Bundle + +plugins { + id("java") + id("java-library") + id("maven-publish") + id("jacoco") + id("biz.aQute.bnd.builder") version "7.1.0" + id("org.gradle.test-retry") version "1.6.4" + id("io.github.gradle-nexus.publish-plugin") version "2.0.0" + id("signing") +} + +def jarVersion = "0.1.0" +group = 'io.synadia' + +def isRelease = System.getenv("BUILD_EVENT") == "release" + +def tc = System.getenv("TARGET_COMPATIBILITY"); +def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) +def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") +def jarAndArtifactName = "partitioned-consumer-groups" + jarEnd + +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = targetCompat +} + +repositories { + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots" } +} + +dependencies { + implementation 'io.nats:jnats:2.25.1' + implementation 'com.google.code.gson:gson:2.11.0' + implementation 'org.jspecify:jspecify:1.0.0' + + testImplementation 'io.nats:jnats-server-runner:3.1.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' + testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' +} + +sourceSets { + main { + java { + srcDirs = ['src/main/java'] + } + } + test { + java { + srcDirs = ['src/test/java'] + } + } +} + +tasks.register('bundle', Bundle) { + from sourceSets.main.output +} + +jar { + bundle { + bnd("Bundle-Name": "io.synadia.partitioned.consumer.groups", + "Bundle-Vendor": "synadia.io", + "Bundle-Description": "NATS JetStream Partitioned Consumer Groups Library for Java", + "Bundle-DocURL": "https://synadia.io" + ) + } +} + +test { + // Use junit platform for unit tests + useJUnitPlatform() + testLogging { + exceptionFormat = 'full' + events "started", "passed", "skipped", "failed" + showStandardStreams = true + } + retry { + failOnPassedAfterRetry = false + maxFailures = 3 + maxRetries = 3 + } + systemProperty 'junit.jupiter.execution.timeout.default', '3m' +} + +javadoc { + source = sourceSets.main.allJava + title = "Synadia Communications Inc. NATS JetStream Partitioned Consumer Groups" + classpath = sourceSets.main.runtimeClasspath +} + +tasks.register('javadocJar', Jar) { + archiveClassifier.set('javadoc') + from javadoc +} + +tasks.register('sourcesJar', Jar) { + archiveClassifier.set('sources') + from sourceSets.main.allSource +} + +tasks.register('testsJar', Jar) { + archiveClassifier.set('tests') + from sourceSets.test.allSource +} + +artifacts { + archives javadocJar, sourcesJar, testsJar +} + +jacoco { + toolVersion = "0.8.12" +} + +jacocoTestReport { + reports { + xml.required = true // coveralls plugin depends on xml format report + html.required = true + } + afterEvaluate { // only report on main library not examples + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, + exclude: ['**/examples**','**/Debug**']) + })) + } +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username = System.getenv('OSSRH_USERNAME') + password = System.getenv('OSSRH_PASSWORD') + } + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact javadocJar + artifact testsJar + pom { + name = jarAndArtifactName + packaging = "jar" + groupId = group + artifactId = jarAndArtifactName + description = "Synadia Communications Inc. NATS JetStream Partitioned Consumer Groups" + url = "https://github.com/synadia-io/orbit.java/tree/main/pcgroups" + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = "synadia" + name = "Synadia" + email = "info@synadia.com" + url = "https://synadia.io" + } + } + scm { + url = "https://github.com/synadia-io/orbit.java" + } + } + } + } +} + +if (isRelease) { + signing { + def signingKeyId = System.getenv('SIGNING_KEY_ID') + def signingKey = System.getenv('SIGNING_KEY') + def signingPassword = System.getenv('SIGNING_PASSWORD') + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + sign configurations.archives + sign publishing.publications.mavenJava + } +} diff --git a/pcgroups/gradle/libs.versions.toml b/pcgroups/gradle/libs.versions.toml new file mode 100644 index 0000000..2cfe86a --- /dev/null +++ b/pcgroups/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.4.5-jre" +junit-jupiter = "5.12.1" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/pcgroups/gradle/wrapper/gradle-wrapper.jar b/pcgroups/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..1b33c55baabb587c669f562ae36f953de2481846 GIT binary patch literal 43764 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/pcgroups/gradlew.bat b/pcgroups/gradlew.bat new file mode 100644 index 0000000..db3a6ac --- /dev/null +++ b/pcgroups/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pcgroups/settings.gradle b/pcgroups/settings.gradle new file mode 100644 index 0000000..4358337 --- /dev/null +++ b/pcgroups/settings.gradle @@ -0,0 +1,13 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://plugins.gradle.org/m2/" } + } + plugins { + id("biz.aQute.bnd.builder") version "7.1.0" + } +} +rootProject.name = 'partitioned-consumer-groups' From 71a4681cd4c2cd3596fef1da439a2e30b302d3c6 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 20 Feb 2026 18:01:24 -0500 Subject: [PATCH 093/135] PCG Build Prep --- .github/workflows/pcg-main.yml | 24 +++++--- .github/workflows/pcg-pr.yml | 30 ++++++---- .github/workflows/pcg-release.yml | 26 ++++++--- pcgroups/pom.xml | 91 ------------------------------- 4 files changed, 53 insertions(+), 118 deletions(-) delete mode 100644 pcgroups/pom.xml diff --git a/.github/workflows/pcg-main.yml b/.github/workflows/pcg-main.yml index 4283082..167bb2f 100644 --- a/.github/workflows/pcg-main.yml +++ b/.github/workflows/pcg-main.yml @@ -10,6 +10,9 @@ on: jobs: build: runs-on: ubuntu-latest + defaults: + run: + working-directory: ./pcgroups env: BUILD_EVENT: ${{ github.event_name }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} @@ -18,18 +21,22 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compile and Test run: | pushd pcgroups @@ -45,3 +52,6 @@ jobs: pushd pcgroups ./gradlew -i publishToSonatype popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/pcg-pr.yml b/.github/workflows/pcg-pr.yml index a667974..d852370 100644 --- a/.github/workflows/pcg-pr.yml +++ b/.github/workflows/pcg-pr.yml @@ -9,6 +9,9 @@ on: jobs: build: runs-on: ubuntu-latest + defaults: + run: + working-directory: ./pcgroups env: BUILD_EVENT: ${{ github.event_name }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} @@ -17,25 +20,28 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 - - name: Compile and Test + uses: actions/checkout@v4 + - name: Build and Test run: | - pushd pcgroups chmod +x gradlew && ./gradlew clean test - popd - name: Verify Javadoc run: | - pushd pcgroups ./gradlew javadoc - popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/pcg-release.yml b/.github/workflows/pcg-release.yml index 7406349..8d67c1f 100644 --- a/.github/workflows/pcg-release.yml +++ b/.github/workflows/pcg-release.yml @@ -7,6 +7,9 @@ on: jobs: build: runs-on: ubuntu-latest + defaults: + run: + working-directory: ./pcgroups env: BUILD_EVENT: "release" OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} @@ -15,18 +18,22 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: '8' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' - name: Install Nats Server run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server nats-server -v - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compile and Test run: | pushd pcgroups @@ -35,5 +42,8 @@ jobs: - name: Verify, Sign and Publish Release run: | pushd pcgroups - ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + ./gradlew -i publishToSonatype closeAndReleaseSonatypeStagingRepository popd + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/pcgroups/pom.xml b/pcgroups/pom.xml deleted file mode 100644 index cba0810..0000000 --- a/pcgroups/pom.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - 4.0.0 - - io.synadia - partitioned-consumer-groups - 0.1.0 - jar - - Partitioned Consumer Groups - NATS JetStream Partitioned Consumer Groups Library for Java - - - 11 - 11 - UTF-8 - 2.25.1 - 2.11.0 - 5.10.2 - - - - - - io.nats - jnats - ${jnats.version} - - - - - com.google.code.gson - gson - ${gson.version} - - - - - org.junit.jupiter - junit-jupiter - ${junit.version} - test - - - io.nats - jnats-server-runner - 3.1.0 - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - 11 - 11 - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.5 - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - - From e16c936c4dbffc7f63b8328705e1360799abb3b4 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 20 Feb 2026 18:36:03 -0500 Subject: [PATCH 094/135] pcgrous and testing reusable workflow --- .github/workflows/pcg-main.yml | 50 ++----------------------- .github/workflows/pcg-pr.yml | 41 ++------------------- .github/workflows/pcg-release.yml | 45 ++--------------------- .github/workflows/workflow-main.yml | 51 ++++++++++++++++++++++++++ .github/workflows/workflow-pr.yml | 48 ++++++++++++++++++++++++ .github/workflows/workflow-release.yml | 48 ++++++++++++++++++++++++ pcgroups/settings.gradle | 2 +- 7 files changed, 160 insertions(+), 125 deletions(-) create mode 100644 .github/workflows/workflow-main.yml create mode 100644 .github/workflows/workflow-pr.yml create mode 100644 .github/workflows/workflow-release.yml diff --git a/.github/workflows/pcg-main.yml b/.github/workflows/pcg-main.yml index 167bb2f..9f30756 100644 --- a/.github/workflows/pcg-main.yml +++ b/.github/workflows/pcg-main.yml @@ -9,49 +9,7 @@ on: jobs: build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./pcgroups - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Check out code - uses: actions/checkout@v4 - - name: Compile and Test - run: | - pushd pcgroups - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify Javadoc - run: | - pushd pcgroups - ./gradlew javadoc - popd - - name: Publish Snapshot - run: | - pushd pcgroups - ./gradlew -i publishToSonatype - popd - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true + uses: ./.github/workflows/workflow-main.yml + with: + project-dir: pcgroups + secrets: inherit diff --git a/.github/workflows/pcg-pr.yml b/.github/workflows/pcg-pr.yml index d852370..0d2c8ff 100644 --- a/.github/workflows/pcg-pr.yml +++ b/.github/workflows/pcg-pr.yml @@ -8,40 +8,7 @@ on: jobs: build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./pcgroups - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Check out code - uses: actions/checkout@v4 - - name: Build and Test - run: | - chmod +x gradlew && ./gradlew clean test - - name: Verify Javadoc - run: | - ./gradlew javadoc - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true + uses: ./.github/workflows/workflow-pr.yml + with: + project-dir: pcgroups + secrets: inherit diff --git a/.github/workflows/pcg-release.yml b/.github/workflows/pcg-release.yml index 8d67c1f..2f64eda 100644 --- a/.github/workflows/pcg-release.yml +++ b/.github/workflows/pcg-release.yml @@ -6,44 +6,7 @@ on: jobs: build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./pcgroups - env: - BUILD_EVENT: "release" - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Check out code - uses: actions/checkout@v4 - - name: Compile and Test - run: | - pushd pcgroups - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify, Sign and Publish Release - run: | - pushd pcgroups - ./gradlew -i publishToSonatype closeAndReleaseSonatypeStagingRepository - popd - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true + uses: ./.github/workflows/workflow-release.yml + with: + project-dir: pcgroups + secrets: inherit diff --git a/.github/workflows/workflow-main.yml b/.github/workflows/workflow-main.yml new file mode 100644 index 0000000..773b51a --- /dev/null +++ b/.github/workflows/workflow-main.yml @@ -0,0 +1,51 @@ +name: Reusable Workflow Main Snapshot Build + +on: + workflow_call: + inputs: + project-dir: + required: true + type: string + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./${{ inputs.project-dir }} + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + - name: Install Nats Server + run: | + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server + nats-server -v + - name: Check out code + uses: actions/checkout@v4 + - name: Compile and Test + run: | + chmod +x gradlew && ./gradlew clean test + - name: Verify Javadoc + run: | + ./gradlew javadoc + - name: Publish Snapshot + run: | + ./gradlew -i publishToSonatype + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/workflow-pr.yml b/.github/workflows/workflow-pr.yml new file mode 100644 index 0000000..13da0d7 --- /dev/null +++ b/.github/workflows/workflow-pr.yml @@ -0,0 +1,48 @@ +name: Reusable Workflow PR Build + +on: + workflow_call: + inputs: + project-dir: + required: true + type: string + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./${{ inputs.project-dir }} + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + - name: Install Nats Server + run: | + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server + nats-server -v + - name: Check out code + uses: actions/checkout@v4 + - name: Build and Test + run: | + chmod +x gradlew && ./gradlew clean test + - name: Verify Javadoc + run: | + ./gradlew javadoc + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/workflow-release.yml b/.github/workflows/workflow-release.yml new file mode 100644 index 0000000..79135e9 --- /dev/null +++ b/.github/workflows/workflow-release.yml @@ -0,0 +1,48 @@ +name: Reusable Workflow Release Build + +on: + workflow_call: + inputs: + project-dir: + required: true + type: string + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./${{ inputs.project-dir }} + env: + BUILD_EVENT: "release" + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + - name: Install Nats Server + run: | + pkill -9 nats-server 2>/dev/null || true + mkdir -p ~/.local/bin + cd $GITHUB_WORKSPACE + git clone https://github.com/nats-io/nats-server.git + cd nats-server + go build -o ~/.local/bin/nats-server + nats-server -v + - name: Check out code + uses: actions/checkout@v4 + - name: Compile and Test + run: | + chmod +x gradlew && ./gradlew clean test + - name: Verify, Sign and Publish Release + run: | + ./gradlew -i publishToSonatype closeAndReleaseSonatypeStagingRepository + - name: Clean up + if: always() + run: pkill -9 nats-server 2>/dev/null || true diff --git a/pcgroups/settings.gradle b/pcgroups/settings.gradle index 4358337..a831682 100644 --- a/pcgroups/settings.gradle +++ b/pcgroups/settings.gradle @@ -10,4 +10,4 @@ pluginManagement { id("biz.aQute.bnd.builder") version "7.1.0" } } -rootProject.name = 'partitioned-consumer-groups' +rootProject.name = 'pcgroups' From 8a920d9508b1b6134c3272717c03611d3327500c Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 20 Feb 2026 18:44:52 -0500 Subject: [PATCH 095/135] pcgrous and testing reusable workflow --- .github/workflows/workflow-main.yml | 2 +- .github/workflows/workflow-pr.yml | 2 +- .github/workflows/workflow-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/workflow-main.yml b/.github/workflows/workflow-main.yml index 773b51a..6eb8071 100644 --- a/.github/workflows/workflow-main.yml +++ b/.github/workflows/workflow-main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ./${{ inputs.project-dir }} + working-directory: ${{ inputs.project-dir }} env: BUILD_EVENT: ${{ github.event_name }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} diff --git a/.github/workflows/workflow-pr.yml b/.github/workflows/workflow-pr.yml index 13da0d7..efef3de 100644 --- a/.github/workflows/workflow-pr.yml +++ b/.github/workflows/workflow-pr.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ./${{ inputs.project-dir }} + working-directory: ${{ inputs.project-dir }} env: BUILD_EVENT: ${{ github.event_name }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} diff --git a/.github/workflows/workflow-release.yml b/.github/workflows/workflow-release.yml index 79135e9..444cae5 100644 --- a/.github/workflows/workflow-release.yml +++ b/.github/workflows/workflow-release.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ./${{ inputs.project-dir }} + working-directory: ${{ inputs.project-dir }} env: BUILD_EVENT: "release" OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} From 2be9f28a166d12ddadfee0ec3ece1fd0c1dc3a11 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 20 Feb 2026 18:47:41 -0500 Subject: [PATCH 096/135] pcgrous and testing reusable workflow --- .github/workflows/workflow-main.yml | 6 +++--- .github/workflows/workflow-pr.yml | 6 +++--- .github/workflows/workflow-release.yml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/workflow-main.yml b/.github/workflows/workflow-main.yml index 6eb8071..b4bbd71 100644 --- a/.github/workflows/workflow-main.yml +++ b/.github/workflows/workflow-main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ${{ inputs.project-dir }} + working-directory: ./${{ inputs.project-dir }} env: BUILD_EVENT: ${{ github.event_name }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} @@ -26,6 +26,8 @@ jobs: with: java-version: '21' distribution: 'temurin' + - name: Check out code + uses: actions/checkout@v4 - name: Install Nats Server run: | pkill -9 nats-server 2>/dev/null || true @@ -35,8 +37,6 @@ jobs: cd nats-server go build -o ~/.local/bin/nats-server nats-server -v - - name: Check out code - uses: actions/checkout@v4 - name: Compile and Test run: | chmod +x gradlew && ./gradlew clean test diff --git a/.github/workflows/workflow-pr.yml b/.github/workflows/workflow-pr.yml index efef3de..f452aab 100644 --- a/.github/workflows/workflow-pr.yml +++ b/.github/workflows/workflow-pr.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ${{ inputs.project-dir }} + working-directory: ./${{ inputs.project-dir }} env: BUILD_EVENT: ${{ github.event_name }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} @@ -26,6 +26,8 @@ jobs: with: java-version: '21' distribution: 'temurin' + - name: Check out code + uses: actions/checkout@v4 - name: Install Nats Server run: | pkill -9 nats-server 2>/dev/null || true @@ -35,8 +37,6 @@ jobs: cd nats-server go build -o ~/.local/bin/nats-server nats-server -v - - name: Check out code - uses: actions/checkout@v4 - name: Build and Test run: | chmod +x gradlew && ./gradlew clean test diff --git a/.github/workflows/workflow-release.yml b/.github/workflows/workflow-release.yml index 444cae5..e78cb78 100644 --- a/.github/workflows/workflow-release.yml +++ b/.github/workflows/workflow-release.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ${{ inputs.project-dir }} + working-directory: ./${{ inputs.project-dir }} env: BUILD_EVENT: "release" OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} @@ -26,6 +26,8 @@ jobs: with: java-version: '21' distribution: 'temurin' + - name: Check out code + uses: actions/checkout@v4 - name: Install Nats Server run: | pkill -9 nats-server 2>/dev/null || true @@ -35,8 +37,6 @@ jobs: cd nats-server go build -o ~/.local/bin/nats-server nats-server -v - - name: Check out code - uses: actions/checkout@v4 - name: Compile and Test run: | chmod +x gradlew && ./gradlew clean test From 0ab60dce03c61666140db8d88dfa6a9d46c0a877 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 20 Feb 2026 19:05:57 -0500 Subject: [PATCH 097/135] testing reusable workflow --- .github/workflows/counters-main.yml | 47 +++----------------------- .github/workflows/counters-pr.yml | 42 +++-------------------- .github/workflows/counters-release.yml | 42 +++-------------------- counters/test.bat | 5 --- 4 files changed, 12 insertions(+), 124 deletions(-) delete mode 100644 counters/test.bat diff --git a/.github/workflows/counters-main.yml b/.github/workflows/counters-main.yml index 715cec7..2b33ae6 100644 --- a/.github/workflows/counters-main.yml +++ b/.github/workflows/counters-main.yml @@ -9,46 +9,7 @@ on: jobs: build: - runs-on: ubuntu-latest - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Check out code - uses: actions/checkout@v4 - - name: Compile and Test - run: | - pushd counters - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify Javadoc - run: | - pushd counters - ./gradlew javadoc - popd - - name: Publish Snapshot - run: | - pushd counters - ./gradlew -i publishToSonatype - popd - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true + uses: ./.github/workflows/workflow-main.yml + with: + project-dir: counters + secrets: inherit diff --git a/.github/workflows/counters-pr.yml b/.github/workflows/counters-pr.yml index 1a02adc..2c7dc55 100644 --- a/.github/workflows/counters-pr.yml +++ b/.github/workflows/counters-pr.yml @@ -8,41 +8,7 @@ on: jobs: build: - runs-on: ubuntu-latest - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Check out code - uses: actions/checkout@v4 - - name: Build and Test - run: | - pushd counters - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify Javadoc - run: | - pushd counters - ./gradlew javadoc - popd - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true + uses: ./.github/workflows/workflow-pr.yml + with: + project-dir: counters + secrets: inherit diff --git a/.github/workflows/counters-release.yml b/.github/workflows/counters-release.yml index dc24cf5..38a26a8 100644 --- a/.github/workflows/counters-release.yml +++ b/.github/workflows/counters-release.yml @@ -6,41 +6,7 @@ on: jobs: build: - runs-on: ubuntu-latest - env: - BUILD_EVENT: "release" - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Check out code - uses: actions/checkout@v4 - - name: Compile and Test - run: | - pushd counters - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify, Sign and Publish Release - run: | - pushd counters - ./gradlew -i publishToSonatype closeAndReleaseSonatypeStagingRepository - popd - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true + uses: ./.github/workflows/workflow-release.yml + with: + project-dir: counters + secrets: inherit diff --git a/counters/test.bat b/counters/test.bat deleted file mode 100644 index 22c2cd1..0000000 --- a/counters/test.bat +++ /dev/null @@ -1,5 +0,0 @@ -call gradlew clean build jacocoTestReport -taskkill /F /IM nats-server.exe -start chrome file:///C:/nats/orbit.java/counter/build/reports/jacoco/test/html/index.html -start chrome file:///C:/nats/orbit.java/counter/build/reports/tests/test/index.html - From cfc43afacd5a4cae050cc1fbae292b61ab69668d Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 21 Feb 2026 09:24:27 -0500 Subject: [PATCH 098/135] Using counters project to test remote reusable workflows --- .github/workflows/counters-main.yml | 2 +- .github/workflows/counters-pr.yml | 2 +- .github/workflows/counters-release.yml | 2 +- counters/README.md | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/counters-main.yml b/.github/workflows/counters-main.yml index 2b33ae6..04569db 100644 --- a/.github/workflows/counters-main.yml +++ b/.github/workflows/counters-main.yml @@ -9,7 +9,7 @@ on: jobs: build: - uses: ./.github/workflows/workflow-main.yml + uses: synadia-io/workflows/.github/workflows/java-standard-main.yml@main with: project-dir: counters secrets: inherit diff --git a/.github/workflows/counters-pr.yml b/.github/workflows/counters-pr.yml index 2c7dc55..be08ff8 100644 --- a/.github/workflows/counters-pr.yml +++ b/.github/workflows/counters-pr.yml @@ -8,7 +8,7 @@ on: jobs: build: - uses: ./.github/workflows/workflow-pr.yml + uses: synadia-io/workflows/.github/workflows/java-standard-pr.yml@main with: project-dir: counters secrets: inherit diff --git a/.github/workflows/counters-release.yml b/.github/workflows/counters-release.yml index 38a26a8..5e357e2 100644 --- a/.github/workflows/counters-release.yml +++ b/.github/workflows/counters-release.yml @@ -6,7 +6,7 @@ on: jobs: build: - uses: ./.github/workflows/workflow-release.yml + uses: synadia-io/workflows/.github/workflows/java-standard-release.yml@main with: project-dir: counters secrets: inherit diff --git a/counters/README.md b/counters/README.md index b343ba1..7cfe330 100644 --- a/counters/README.md +++ b/counters/README.md @@ -86,3 +86,5 @@ public CounterIterator iterateEntries(List subjects, Duration timeoutFir --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. + +Updated: 2/21/2026 \ No newline at end of file From 3e85802d29d8426623ff1e1cecb57b38896833cb Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 21 Feb 2026 09:29:04 -0500 Subject: [PATCH 099/135] Using counters project to test remote reusable workflows --- counters/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/counters/README.md b/counters/README.md index 7cfe330..b343ba1 100644 --- a/counters/README.md +++ b/counters/README.md @@ -86,5 +86,3 @@ public CounterIterator iterateEntries(List subjects, Duration timeoutFir --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. - -Updated: 2/21/2026 \ No newline at end of file From 56861b0bf7fe07b24698a3b6f3140c138a87cabe Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 21 Feb 2026 11:12:43 -0500 Subject: [PATCH 100/135] schedule-message project to reusable workflows --- .github/workflows/sm-main.yml | 40 ++------------------ .github/workflows/sm-pr.yml | 35 ++---------------- .github/workflows/sm-release.yml | 35 ++---------------- .github/workflows/workflow-main.yml | 51 -------------------------- .github/workflows/workflow-pr.yml | 48 ------------------------ .github/workflows/workflow-release.yml | 48 ------------------------ schedule-message/build.gradle | 2 +- 7 files changed, 13 insertions(+), 246 deletions(-) delete mode 100644 .github/workflows/workflow-main.yml delete mode 100644 .github/workflows/workflow-pr.yml delete mode 100644 .github/workflows/workflow-release.yml diff --git a/.github/workflows/sm-main.yml b/.github/workflows/sm-main.yml index 41d2ca1..ba72d3f 100644 --- a/.github/workflows/sm-main.yml +++ b/.github/workflows/sm-main.yml @@ -9,39 +9,7 @@ on: jobs: build: - runs-on: ubuntu-latest - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 - with: - java-version: '8' - distribution: 'adopt' - - name: Install Nats Server - run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin - nats-server -v - - name: Check out code - uses: actions/checkout@v3 - - name: Compile and Test - run: | - pushd schedule-message - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify Javadoc - run: | - pushd schedule-message - ./gradlew javadoc - popd - - name: Publish Snapshot - run: | - pushd schedule-message - ./gradlew -i publishToSonatype - popd + uses: synadia-io/workflows/.github/workflows/java-standard-main.yml@main + with: + project-dir: schedule-message + secrets: inherit diff --git a/.github/workflows/sm-pr.yml b/.github/workflows/sm-pr.yml index 6d60b9e..bf7779f 100644 --- a/.github/workflows/sm-pr.yml +++ b/.github/workflows/sm-pr.yml @@ -8,34 +8,7 @@ on: jobs: build: - runs-on: ubuntu-latest - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 - with: - java-version: '8' - distribution: 'adopt' - - name: Install Nats Server - run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin - nats-server -v - - name: Check out code - uses: actions/checkout@v3 - - name: Compile and Test - run: | - pushd schedule-message - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify Javadoc - run: | - pushd schedule-message - ./gradlew javadoc - popd + uses: synadia-io/workflows/.github/workflows/java-standard-pr.yml@main + with: + project-dir: schedule-message + secrets: inherit diff --git a/.github/workflows/sm-release.yml b/.github/workflows/sm-release.yml index e3abcd9..b342e3d 100644 --- a/.github/workflows/sm-release.yml +++ b/.github/workflows/sm-release.yml @@ -6,34 +6,7 @@ on: jobs: build: - runs-on: ubuntu-latest - env: - BUILD_EVENT: "release" - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 - with: - java-version: '8' - distribution: 'adopt' - - name: Install Nats Server - run: | - curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh - sudo mv nats-server /usr/local/bin - nats-server -v - - name: Check out code - uses: actions/checkout@v3 - - name: Compile and Test - run: | - pushd schedule-message - chmod +x gradlew && ./gradlew clean test - popd - - name: Verify, Sign and Publish Release - run: | - pushd schedule-message - ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository - popd + uses: synadia-io/workflows/.github/workflows/java-standard-release.yml@main + with: + project-dir: schedule-message + secrets: inherit diff --git a/.github/workflows/workflow-main.yml b/.github/workflows/workflow-main.yml deleted file mode 100644 index b4bbd71..0000000 --- a/.github/workflows/workflow-main.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Reusable Workflow Main Snapshot Build - -on: - workflow_call: - inputs: - project-dir: - required: true - type: string - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./${{ inputs.project-dir }} - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Check out code - uses: actions/checkout@v4 - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Compile and Test - run: | - chmod +x gradlew && ./gradlew clean test - - name: Verify Javadoc - run: | - ./gradlew javadoc - - name: Publish Snapshot - run: | - ./gradlew -i publishToSonatype - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/workflow-pr.yml b/.github/workflows/workflow-pr.yml deleted file mode 100644 index f452aab..0000000 --- a/.github/workflows/workflow-pr.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Reusable Workflow PR Build - -on: - workflow_call: - inputs: - project-dir: - required: true - type: string - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./${{ inputs.project-dir }} - env: - BUILD_EVENT: ${{ github.event_name }} - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Check out code - uses: actions/checkout@v4 - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Build and Test - run: | - chmod +x gradlew && ./gradlew clean test - - name: Verify Javadoc - run: | - ./gradlew javadoc - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true diff --git a/.github/workflows/workflow-release.yml b/.github/workflows/workflow-release.yml deleted file mode 100644 index e78cb78..0000000 --- a/.github/workflows/workflow-release.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Reusable Workflow Release Build - -on: - workflow_call: - inputs: - project-dir: - required: true - type: string - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./${{ inputs.project-dir }} - env: - BUILD_EVENT: "release" - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Check out code - uses: actions/checkout@v4 - - name: Install Nats Server - run: | - pkill -9 nats-server 2>/dev/null || true - mkdir -p ~/.local/bin - cd $GITHUB_WORKSPACE - git clone https://github.com/nats-io/nats-server.git - cd nats-server - go build -o ~/.local/bin/nats-server - nats-server -v - - name: Compile and Test - run: | - chmod +x gradlew && ./gradlew clean test - - name: Verify, Sign and Publish Release - run: | - ./gradlew -i publishToSonatype closeAndReleaseSonatypeStagingRepository - - name: Clean up - if: always() - run: pkill -9 nats-server 2>/dev/null || true diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index b249c47..c868c67 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -37,7 +37,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.25.1' implementation 'org.jspecify:jspecify:1.0.0' - implementation 'io.synadia:counters:0.2.1-SNAPSHOT' + implementation 'io.synadia:counters:0.2.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' From df1f1e1e8900f7b9295a02deb147aa84c3370865 Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 21 Feb 2026 11:25:54 -0500 Subject: [PATCH 101/135] update after release --- README.md | 14 +++++++------- counters/README.md | 4 ++-- counters/build.gradle | 2 +- schedule-message/README.md | 4 ++-- schedule-message/build.gradle | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0effd8c..e5d0bf0 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ client. Note that these libraries will evolve rapidly and API guarantees are gen | Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | | Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | | Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | -| Batch Publish | Publish an atomic batch | [README.md](dirrrrrrr/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | -| Distributed Counters | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.2.0 | 0.2.1-SNAPSHOT | -| Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.2 | 0.0.3-SNAPSHOT | +| Batch Publish | Publish an atomic batch | [README.md](batch-publish/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | +| Distributed Counters | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.2.2 | 0.2.3-SNAPSHOT | +| Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.3 | 0.0.4-SNAPSHOT | | Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | | Partitioned Consumer Groups | Partitioned Consumer Group funcitionality for JetStream | [README.md](pcgroups/README.md) | 0.0.1 | 0.0.1-SNAPSHOT | @@ -97,8 +97,8 @@ Utility to publish an atomic batch, a group of up to 1000 messages Utility to take leverage the distributed counter functionality. -**Current Release**: 0.2.0 -  **Current Snapshot**: 0.2.1-SNAPSHOT +**Current Release**: 0.2.2 +  **Current Snapshot**: 0.2.3-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=counters/README.md)](counters/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-00BC8E?labelColor=grey&style=flat) @@ -110,8 +110,8 @@ Utility to take leverage the distributed counter functionality. Utility to leverage the ability to schedule a message to be published at a later time. Eventually the ability to schedule a message to publish based on a cron or schedule. -**Current Release**: 0.0.2 -  **Current Snapshot**: 0.0.3-SNAPSHOT +**Current Release**: 0.0.3 +  **Current Snapshot**: 0.0.4-SNAPSHOT [![README](https://img.shields.io/badge/README-blue?style=flat&link=schedule-message/README.md)](schedule-message/README.md) ![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) diff --git a/counters/README.md b/counters/README.md index b343ba1..d278513 100644 --- a/counters/README.md +++ b/counters/README.md @@ -6,8 +6,8 @@ Utility to take advantage of the distributed counter functionality. https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md -**Current Release**: 0.2.1 -  **Current Snapshot**: 0.2.2-SNAPSHOT +**Current Release**: 0.2.2 +  **Current Snapshot**: 0.2.3-SNAPSHOT   **Gradle and Maven** `io.synadia:counters` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/counters/build.gradle b/counters/build.gradle index 996c923..cb00731 100644 --- a/counters/build.gradle +++ b/counters/build.gradle @@ -11,7 +11,7 @@ plugins { id("signing") } -def jarVersion = "0.2.2" +def jarVersion = "0.2.3" group = 'io.synadia' def isRelease = System.getenv("BUILD_EVENT") == "release" diff --git a/schedule-message/README.md b/schedule-message/README.md index fd4c8be..dfb7953 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -7,8 +7,8 @@ Eventually the ability to schedule a message to publish based on a cron or sched https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md -**Current Release**: 0.0.2 -  **Current Snapshot**: 0.0.3-SNAPSHOT +**Current Release**: 0.0.3 +  **Current Snapshot**: 0.0.4-SNAPSHOT   **Gradle and Maven** `io.synadia:scheduled-message` [Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index c868c67..f42c512 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -11,7 +11,7 @@ plugins { id("signing") } -def jarVersion = "0.0.3" +def jarVersion = "0.0.4" group = 'io.synadia' def isRelease = System.getenv("BUILD_EVENT") == "release" From 69db6814d471ffe7efca36a85bb3f66734f5246a Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 23 Feb 2026 12:46:57 -0500 Subject: [PATCH 102/135] Remove GSON, use internal Json tool --- pcgroups/build.gradle | 30 +--- .../io/synadia/pcg/ElasticConsumerGroup.java | 54 +++----- .../pcg/ElasticConsumerGroupConfig.java | 130 ++++++++++++------ .../java/io/synadia/pcg/MemberMapping.java | 49 ++++++- .../io/synadia/pcg/StaticConsumerGroup.java | 21 +-- .../pcg/StaticConsumerGroupConfig.java | 80 +++++++---- .../synadia/pcg/ElasticConsumerGroupTest.java | 38 ++--- .../java/io/synadia/pcg/IntegrationTest.java | 17 ++- .../io/synadia/pcg/PartitionUtilsTest.java | 9 +- .../synadia/pcg/StaticConsumerGroupTest.java | 30 ++-- 10 files changed, 280 insertions(+), 178 deletions(-) diff --git a/pcgroups/build.gradle b/pcgroups/build.gradle index 383c6fe..c84dda9 100644 --- a/pcgroups/build.gradle +++ b/pcgroups/build.gradle @@ -19,7 +19,7 @@ def isRelease = System.getenv("BUILD_EVENT") == "release" def tc = System.getenv("TARGET_COMPATIBILITY"); def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") -def jarAndArtifactName = "partitioned-consumer-groups" + jarEnd +def jarAndArtifactName = "pcgroups" + jarEnd version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. @@ -36,10 +36,10 @@ repositories { dependencies { implementation 'io.nats:jnats:2.25.1' - implementation 'com.google.code.gson:gson:2.11.0' implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:3.1.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } @@ -66,28 +66,18 @@ jar { bnd("Bundle-Name": "io.synadia.partitioned.consumer.groups", "Bundle-Vendor": "synadia.io", "Bundle-Description": "NATS JetStream Partitioned Consumer Groups Library for Java", - "Bundle-DocURL": "https://synadia.io" + "Bundle-DocURL": "https://github.com/synadia-io/orbit.java/tree/main/pcgroups", + "Target-Compatibility": "Java " + targetCompat ) } } test { - // Use junit platform for unit tests useJUnitPlatform() - testLogging { - exceptionFormat = 'full' - events "started", "passed", "skipped", "failed" - showStandardStreams = true - } - retry { - failOnPassedAfterRetry = false - maxFailures = 3 - maxRetries = 3 - } - systemProperty 'junit.jupiter.execution.timeout.default', '3m' } javadoc { + options.overview = 'src/main/javadoc/overview.html' // relative to source root source = sourceSets.main.allJava title = "Synadia Communications Inc. NATS JetStream Partitioned Consumer Groups" classpath = sourceSets.main.runtimeClasspath @@ -103,13 +93,8 @@ tasks.register('sourcesJar', Jar) { from sourceSets.main.allSource } -tasks.register('testsJar', Jar) { - archiveClassifier.set('tests') - from sourceSets.test.allSource -} - artifacts { - archives javadocJar, sourcesJar, testsJar + archives javadocJar, sourcesJar } jacoco { @@ -146,10 +131,9 @@ publishing { from components.java artifact sourcesJar artifact javadocJar - artifact testsJar pom { name = jarAndArtifactName - packaging = "jar" + packaging = 'jar' groupId = group artifactId = jarAndArtifactName description = "Synadia Communications Inc. NATS JetStream Partitioned Consumer Groups" diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java index b26a668..09d5042 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java @@ -13,15 +13,12 @@ package io.synadia.pcg; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import io.nats.client.*; import io.nats.client.api.*; import io.nats.client.impl.Headers; import io.synadia.pcg.exceptions.ConsumerGroupException; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -41,7 +38,6 @@ public class ElasticConsumerGroup { private static final Logger LOGGER = Logger.getLogger(ElasticConsumerGroup.class.getName()); - private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); private ElasticConsumerGroup() { // Utility class @@ -96,27 +92,22 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName String key = composeKey(streamName, consumerGroupName); // Check if config already exists - KeyValueEntry entry = kv.get(key); - if (entry != null) { - String json = new String(entry.getValue(), StandardCharsets.UTF_8); - ElasticConsumerGroupConfig existingConfig = GSON.fromJson(json, ElasticConsumerGroupConfig.class); - - // Verify the config matches + ElasticConsumerGroupConfig existingConfig = ElasticConsumerGroupConfig.instance(kv.get(key)); + if (existingConfig != null) { if (existingConfig.getMaxMembers() != maxMembers || - !Objects.equals(existingConfig.getFilter(), filter) || - existingConfig.getMaxBufferedMsgs() != maxBufferedMsgs || - existingConfig.getMaxBufferedBytes() != maxBufferedBytes || - !Arrays.equals(existingConfig.getPartitioningWildcards(), partitioningWildcards)) { + !Objects.equals(existingConfig.getFilter(), filter) || + existingConfig.getMaxBufferedMessages() != maxBufferedMsgs || + existingConfig.getMaxBufferedBytes() != maxBufferedBytes || + !Arrays.equals(existingConfig.getPartitioningWildcards(), partitioningWildcards)) { throw new ConsumerGroupException( - "the existing elastic consumer group config can not be updated to the requested one, " + - "please delete the existing elastic consumer group and create a new one"); + "the existing elastic consumer group config can not be updated to the requested one, " + + "please delete the existing elastic consumer group and create a new one"); } return existingConfig; } // Create the config entry - String payload = GSON.toJson(config); - kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + kv.put(key, config.serialize()); // Create the work queue stream with subject transform String workQueueStreamName = composeCGSName(streamName, consumerGroupName); @@ -305,9 +296,8 @@ public static List addMembers(Connection nc, String streamName, String c List newMembers = new ArrayList<>(existingMembers); config.setMembers(newMembers); - String payload = GSON.toJson(config); String key = composeKey(streamName, consumerGroupName); - kv.update(key, payload.getBytes(StandardCharsets.UTF_8), config.getRevision()); + kv.update(key, config.serialize(), config.getRevision()); return newMembers; } @@ -350,9 +340,8 @@ public static List deleteMembers(Connection nc, String streamName, Strin config.setMembers(newMembers); - String payload = GSON.toJson(config); String key = composeKey(streamName, consumerGroupName); - kv.update(key, payload.getBytes(StandardCharsets.UTF_8), config.getRevision()); + kv.update(key, config.serialize(), config.getRevision()); return newMembers; } @@ -382,9 +371,8 @@ public static void setMemberMappings(Connection nc, String streamName, String co config.setMemberMappings(memberMappings); config.validate(); - String payload = GSON.toJson(config); String key = composeKey(streamName, consumerGroupName); - kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + kv.put(key, config.serialize()); } /** @@ -408,9 +396,8 @@ public static void deleteMemberMappings(Connection nc, String streamName, String config.setMemberMappings(new ArrayList<>()); - String payload = GSON.toJson(config); String key = composeKey(streamName, consumerGroupName); - kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + kv.put(key, config.serialize()); } /** @@ -538,17 +525,11 @@ private static ElasticConsumerGroupConfig getConfigFromKV(KeyValue kv, String st } String key = composeKey(streamName, consumerGroupName); - KeyValueEntry entry = kv.get(key); - - if (entry == null) { + ElasticConsumerGroupConfig config = ElasticConsumerGroupConfig.instance(kv.get(key)); + if (config == null) { throw new ConsumerGroupException("error getting the elastic consumer group's config: not found"); } - - String json = new String(entry.getValue(), StandardCharsets.UTF_8); - ElasticConsumerGroupConfig config = GSON.fromJson(json, ElasticConsumerGroupConfig.class); - config.setRevision(entry.getRevision()); config.validate(); - return config; } @@ -730,14 +711,13 @@ private void processWatcherUpdate(KeyValueEntry entry) { } try { - String json = new String(entry.getValue(), StandardCharsets.UTF_8); - ElasticConsumerGroupConfig newConfig = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + ElasticConsumerGroupConfig newConfig = ElasticConsumerGroupConfig.instance(entry); newConfig.validate(); // Check if critical config changed (immutable fields) if (newConfig.getMaxMembers() != config.getMaxMembers() || !Objects.equals(newConfig.getFilter(), config.getFilter()) || - newConfig.getMaxBufferedMsgs() != config.getMaxBufferedMsgs() || + newConfig.getMaxBufferedMessages() != config.getMaxBufferedMessages() || newConfig.getMaxBufferedBytes() != config.getMaxBufferedBytes() || !Arrays.equals(newConfig.getPartitioningWildcards(), config.getPartitioningWildcards())) { stopConsuming(); diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java index 29e3c21..579001f 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java @@ -13,41 +13,59 @@ package io.synadia.pcg; -import com.google.gson.annotations.SerializedName; +import io.nats.client.api.KeyValueEntry; +import io.nats.client.support.*; import io.synadia.pcg.exceptions.ConsumerGroupException; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.util.*; +import static io.nats.client.support.JsonUtils.*; +import static io.nats.client.support.JsonValueUtils.*; + /** * Configuration for an elastic consumer group. * JSON structure must be compatible with the Go version. */ -public class ElasticConsumerGroupConfig { +public class ElasticConsumerGroupConfig implements JsonSerializable { + static final String MAX_MEMBERS = "max_members"; + static final String FILTER = "filter"; + static final String PARTITIONING_WILDCARDS = "partitioning_wildcards"; + static final String MAX_BUFFERED_MSG = "max_buffered_msg"; + static final String MAX_BUFFERED_BYTES = "max_buffered_bytes"; + static final String MEMBERS = "members"; + static final String MEMBER_MAPPINGS = "member_mappings"; - @SerializedName("max_members") private int maxMembers; - - @SerializedName("filter") private String filter; - - @SerializedName("partitioning_wildcards") private int[] partitioningWildcards; - - @SerializedName("max_buffered_msg") - private long maxBufferedMsgs; - - @SerializedName("max_buffered_bytes") + private long maxBufferedMessages; private long maxBufferedBytes; - - @SerializedName("members") private List members; - - @SerializedName("member_mappings") private List memberMappings; // Internal revision number, not serialized private transient long revision; + @Nullable + public static ElasticConsumerGroupConfig instance(KeyValueEntry entry) throws JsonParseException { + if (entry != null) { + byte[] json = entry.getValue(); + if (json != null) { + ElasticConsumerGroupConfig config = instance(json); + config.setRevision(entry.getRevision()); + return config; + } + } + return null; + } + + @NonNull + public static ElasticConsumerGroupConfig instance(byte @NonNull[] json) throws JsonParseException { + return new ElasticConsumerGroupConfig(JsonParser.parse(json)); + } + public ElasticConsumerGroupConfig() { this.partitioningWildcards = new int[0]; this.members = new ArrayList<>(); @@ -55,15 +73,30 @@ public ElasticConsumerGroupConfig() { } public ElasticConsumerGroupConfig(int maxMembers, String filter, int[] partitioningWildcards, - long maxBufferedMsgs, long maxBufferedBytes, + long maxBufferedMessages, long maxBufferedBytes, List members, List memberMappings) { this.maxMembers = maxMembers; this.filter = filter; this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; - this.maxBufferedMsgs = maxBufferedMsgs; + this.maxBufferedMessages = maxBufferedMessages; this.maxBufferedBytes = maxBufferedBytes; - this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); - this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + this.members = members == null ? new ArrayList<>() : new ArrayList<>(members); + this.memberMappings = memberMappings == null ? new ArrayList<>() : new ArrayList<>(memberMappings); + } + + public ElasticConsumerGroupConfig(JsonValue jv) { + this.maxMembers = JsonValueUtils.readInteger(jv, MAX_MEMBERS, 0); + this.filter = JsonValueUtils.readString(jv, FILTER); + List integers = read(jv, PARTITIONING_WILDCARDS, v -> listOf(v, JsonValueUtils::getInteger)); + this.partitioningWildcards = new int[integers.size()]; + for (int x = 0; x < integers.size(); x++) { + Integer i = integers.get(x); + this.partitioningWildcards[x] = i == null ? 0 : i; + } + this.maxBufferedMessages = JsonValueUtils.readLong(jv, MAX_BUFFERED_MSG, 0); + this.maxBufferedBytes = JsonValueUtils.readLong(jv, MAX_BUFFERED_BYTES, 0); + this.members = JsonValueUtils.readStringList(jv, MEMBERS); + this.memberMappings = MemberMapping.listOfOrEmptyList(readValue(jv, MEMBER_MAPPINGS)); } public int getMaxMembers() { @@ -83,19 +116,19 @@ public void setFilter(String filter) { } public int[] getPartitioningWildcards() { - return partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + return partitioningWildcards.clone(); } public void setPartitioningWildcards(int[] partitioningWildcards) { - this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + this.partitioningWildcards = partitioningWildcards == null ? new int[0] : partitioningWildcards.clone(); } - public long getMaxBufferedMsgs() { - return maxBufferedMsgs; + public long getMaxBufferedMessages() { + return maxBufferedMessages; } - public void setMaxBufferedMsgs(long maxBufferedMsgs) { - this.maxBufferedMsgs = maxBufferedMsgs; + public void setMaxBufferedMessages(long maxBufferedMessages) { + this.maxBufferedMessages = maxBufferedMessages; } public long getMaxBufferedBytes() { @@ -111,15 +144,15 @@ public List getMembers() { } public void setMembers(List members) { - this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); + this.members = members == null ? new ArrayList<>() : new ArrayList<>(members); } public List getMemberMappings() { - return memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + return new ArrayList<>(memberMappings); } public void setMemberMappings(List memberMappings) { - this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + this.memberMappings = memberMappings == null ? new ArrayList<>() : new ArrayList<>(memberMappings); } public long getRevision() { @@ -134,17 +167,14 @@ public void setRevision(long revision) { * Checks if the given member name is in the current membership. */ public boolean isInMembership(String name) { - if (memberMappings != null && !memberMappings.isEmpty()) { + if (!memberMappings.isEmpty()) { for (MemberMapping mapping : memberMappings) { if (mapping.getMember().equals(name)) { return true; } } } - if (members != null && !members.isEmpty()) { - return members.contains(name); - } - return false; + return members.contains(name); } /** @@ -192,8 +222,8 @@ public void validate() throws ConsumerGroupException { } // Validate that only one of members or member mappings is provided - boolean hasMembers = members != null && !members.isEmpty(); - boolean hasMemberMappings = memberMappings != null && !memberMappings.isEmpty(); + boolean hasMembers = !members.isEmpty(); + boolean hasMemberMappings = !memberMappings.isEmpty(); if (hasMembers && hasMemberMappings) { throw new ConsumerGroupException("either members or member mappings must be provided, not both"); @@ -201,7 +231,7 @@ public void validate() throws ConsumerGroupException { // Validate member mappings if (hasMemberMappings) { - if (memberMappings.size() < 1 || memberMappings.size() > maxMembers) { + if (memberMappings.size() > maxMembers) { throw new ConsumerGroupException("the number of member mappings must be between 1 and the max number of members"); } @@ -257,13 +287,33 @@ public String getPartitioningTransformDest() { return "{{Partition(" + maxMembers + "," + wildcardList + ")}}." + destFromFilter; } + @Override + public String toJson() { + StringBuilder sb = beginJson(); + addField(sb, MAX_MEMBERS, maxMembers); + addField(sb, FILTER, filter); + if (partitioningWildcards != null && partitioningWildcards.length > 0) { + sb.append("\"").append(PARTITIONING_WILDCARDS).append("\":["); + for (int i = 0; i < partitioningWildcards.length; i++) { + if (i > 0) sb.append(","); + sb.append(partitioningWildcards[i]); + } + sb.append("],"); + } + addField(sb, MAX_BUFFERED_MSG, maxBufferedMessages); + addField(sb, MAX_BUFFERED_BYTES, maxBufferedBytes); + addStrings(sb, MEMBERS, members); + addJsons(sb, MEMBER_MAPPINGS, memberMappings); + return endJson(sb).toString(); + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ElasticConsumerGroupConfig that = (ElasticConsumerGroupConfig) o; return maxMembers == that.maxMembers && - maxBufferedMsgs == that.maxBufferedMsgs && + maxBufferedMessages == that.maxBufferedMessages && maxBufferedBytes == that.maxBufferedBytes && Objects.equals(filter, that.filter) && Arrays.equals(partitioningWildcards, that.partitioningWildcards) && @@ -273,7 +323,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = Objects.hash(maxMembers, filter, maxBufferedMsgs, maxBufferedBytes, members, memberMappings); + int result = Objects.hash(maxMembers, filter, maxBufferedMessages, maxBufferedBytes, members, memberMappings); result = 31 * result + Arrays.hashCode(partitioningWildcards); return result; } @@ -284,7 +334,7 @@ public String toString() { "maxMembers=" + maxMembers + ", filter='" + filter + '\'' + ", partitioningWildcards=" + Arrays.toString(partitioningWildcards) + - ", maxBufferedMsgs=" + maxBufferedMsgs + + ", maxBufferedMsgs=" + maxBufferedMessages + ", maxBufferedBytes=" + maxBufferedBytes + ", members=" + members + ", memberMappings=" + memberMappings + diff --git a/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java b/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java index adca2f3..c81d3a4 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java +++ b/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java @@ -13,30 +13,67 @@ package io.synadia.pcg; -import com.google.gson.annotations.SerializedName; +import io.nats.client.support.JsonSerializable; +import io.nats.client.support.JsonValue; +import io.nats.client.support.JsonValueUtils; +import org.jspecify.annotations.NonNull; + +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; +import static io.nats.client.support.JsonUtils.*; +import static io.nats.client.support.JsonValueUtils.*; +import static io.nats.client.support.JsonValueUtils.readString; + /** * Represents a mapping between a member name and its assigned partitions. * JSON structure must be compatible with the Go version. */ -public class MemberMapping { +public class MemberMapping implements JsonSerializable { + static final String MEMBER = "member"; + static final String PARTITIONS = "partitions"; - @SerializedName("member") private String member; - - @SerializedName("partitions") private int[] partitions; - public MemberMapping() { + static List listOfOrEmptyList(JsonValue jv) { + return JsonValueUtils.listOf(jv, MemberMapping::new); } + public MemberMapping() {} + public MemberMapping(String member, int[] partitions) { this.member = member; this.partitions = partitions != null ? partitions.clone() : new int[0]; } + public MemberMapping(JsonValue jv) { + this.member = readString(jv, MEMBER); + List integers = read(jv, PARTITIONS, v -> listOf(v, JsonValueUtils::getInteger)); + this.partitions = new int[integers.size()]; + for (int x = 0; x < integers.size(); x++) { + Integer i = integers.get(x); + this.partitions[x] = i == null ? 0 : i; + } + } + + @Override + @NonNull + public String toJson() { + StringBuilder sb = beginJson(); + addField(sb, MEMBER, member); + if (partitions.length > 0) { + List integers = new ArrayList(partitions.length); + for (int i : partitions) { + integers.add(i); + } + _addList(sb, PARTITIONS, integers, StringBuilder::append); + } + return endJson(sb).toString(); + } + public String getMember() { return member; } diff --git a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java index a9f5d15..e9583b7 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java @@ -13,15 +13,12 @@ package io.synadia.pcg; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import io.nats.client.*; import io.nats.client.api.*; import io.nats.client.impl.Headers; import io.synadia.pcg.exceptions.ConsumerGroupException; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -40,7 +37,6 @@ public class StaticConsumerGroup { private static final Logger LOGGER = Logger.getLogger(StaticConsumerGroup.class.getName()); - private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); private StaticConsumerGroup() { // Utility class @@ -89,10 +85,8 @@ public static StaticConsumerGroupConfig create(Connection nc, String streamName, String key = composeKey(streamName, consumerGroupName); // Check if config already exists - KeyValueEntry entry = kv.get(key); - if (entry != null) { - String json = new String(entry.getValue(), StandardCharsets.UTF_8); - StaticConsumerGroupConfig existingConfig = GSON.fromJson(json, StaticConsumerGroupConfig.class); + StaticConsumerGroupConfig existingConfig = StaticConsumerGroupConfig.instance(kv.get(key)); + if (existingConfig != null) { // Verify the config matches if (!configsMatch(existingConfig, config)) { @@ -102,8 +96,7 @@ public static StaticConsumerGroupConfig create(Connection nc, String streamName, } // Create the config entry - String payload = GSON.toJson(config); - kv.put(key, payload.getBytes(StandardCharsets.UTF_8)); + kv.put(key, config.serialize()); return config; } @@ -299,8 +292,7 @@ private static StaticConsumerGroupConfig getConfigFromKV(KeyValue kv, String str throw new ConsumerGroupException("error getting the static consumer group's config: not found"); } - String json = new String(entry.getValue(), StandardCharsets.UTF_8); - StaticConsumerGroupConfig config = GSON.fromJson(json, StaticConsumerGroupConfig.class); + StaticConsumerGroupConfig config = StaticConsumerGroupConfig.instance(entry); config.validate(); return config; @@ -374,7 +366,7 @@ private void joinMemberConsumer() throws IOException, JetStreamApiException, Int // Create the durable consumer explicitly (matching Go's js.CreateConsumer) JetStreamManagement jsm = nc.jetStreamManagement(); - System.out.printf("Creating consumer %s with filters %s and priority group %s%n\n", consumerName, filters, PRIORITY_GROUP_NAME); +// System.out.printf("Creating consumer %s with filters %s and priority group %s%n\n", consumerName, filters, PRIORITY_GROUP_NAME); jsm.createConsumer(streamName, cc); // Get consumer context and start consuming @@ -425,8 +417,7 @@ public void watch(KeyValueEntry entry) { } try { - String json = new String(entry.getValue(), StandardCharsets.UTF_8); - StaticConsumerGroupConfig newConfig = GSON.fromJson(json, StaticConsumerGroupConfig.class); + StaticConsumerGroupConfig newConfig = StaticConsumerGroupConfig.instance(entry); newConfig.validate(); // Check if critical config changed diff --git a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java index 3f0ad2d..dc0c885 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java +++ b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java @@ -13,29 +13,48 @@ package io.synadia.pcg; -import com.google.gson.annotations.SerializedName; +import io.nats.client.api.KeyValueEntry; +import io.nats.client.support.*; import io.synadia.pcg.exceptions.ConsumerGroupException; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.util.*; +import static io.nats.client.support.JsonUtils.*; +import static io.nats.client.support.JsonValueUtils.readValue; + /** * Configuration for a static consumer group. * JSON structure must be compatible with the Go version. */ -public class StaticConsumerGroupConfig { +public class StaticConsumerGroupConfig implements JsonSerializable { + static final String MAX_MEMBERS = "max_members"; + static final String FILTER = "filter"; + static final String MEMBERS = "members"; + static final String MEMBER_MAPPINGS = "member_mappings"; - @SerializedName("max_members") private int maxMembers; - - @SerializedName("filter") private String filter; - - @SerializedName("members") private List members; - - @SerializedName("member_mappings") private List memberMappings; + @Nullable + public static StaticConsumerGroupConfig instance(KeyValueEntry entry) throws JsonParseException { + if (entry != null) { + byte[] json = entry.getValue(); + if (json != null) { + return instance(json); + } + } + return null; + } + + @NonNull + public static StaticConsumerGroupConfig instance(byte @NonNull[] json) throws JsonParseException { + return new StaticConsumerGroupConfig(JsonParser.parse(json)); + } + public StaticConsumerGroupConfig() { this.members = new ArrayList<>(); this.memberMappings = new ArrayList<>(); @@ -44,8 +63,26 @@ public StaticConsumerGroupConfig() { public StaticConsumerGroupConfig(int maxMembers, String filter, List members, List memberMappings) { this.maxMembers = maxMembers; this.filter = filter; - this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); - this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + this.members = members == null ? new ArrayList<>() : new ArrayList<>(members); + this.memberMappings = memberMappings == null ? new ArrayList<>() : new ArrayList<>(memberMappings); + } + + public StaticConsumerGroupConfig(JsonValue jv) { + this.maxMembers = JsonValueUtils.readInteger(jv, MAX_MEMBERS, 0); + this.filter = JsonValueUtils.readString(jv, FILTER); + this.members = JsonValueUtils.readStringList(jv, MEMBERS); + this.memberMappings = MemberMapping.listOfOrEmptyList(readValue(jv, MEMBER_MAPPINGS)); + } + + @Override + @NonNull + public String toJson() { + StringBuilder sb = beginJson(); + addField(sb, MAX_MEMBERS, maxMembers); + addField(sb, FILTER, filter); + addStrings(sb, MEMBERS, members); + addJsons(sb, MEMBER_MAPPINGS, memberMappings); + return endJson(sb).toString(); } public int getMaxMembers() { @@ -65,36 +102,33 @@ public void setFilter(String filter) { } public List getMembers() { - return members != null ? new ArrayList<>(members) : new ArrayList<>(); + return new ArrayList<>(members); } public void setMembers(List members) { - this.members = members != null ? new ArrayList<>(members) : new ArrayList<>(); + this.members = members == null ? new ArrayList<>() : new ArrayList<>(members); } public List getMemberMappings() { - return memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + return new ArrayList<>(memberMappings); } public void setMemberMappings(List memberMappings) { - this.memberMappings = memberMappings != null ? new ArrayList<>(memberMappings) : new ArrayList<>(); + this.memberMappings = new ArrayList<>(memberMappings); } /** * Checks if the given member name is in the current membership. */ public boolean isInMembership(String name) { - if (memberMappings != null && !memberMappings.isEmpty()) { + if (!memberMappings.isEmpty()) { for (MemberMapping mapping : memberMappings) { if (mapping.getMember().equals(name)) { return true; } } } - if (members != null && !members.isEmpty()) { - return members.contains(name); - } - return false; + return members.contains(name); } /** @@ -109,8 +143,8 @@ public void validate() throws ConsumerGroupException { } // Validate that only one of members or member mappings is provided - boolean hasMembers = members != null && !members.isEmpty(); - boolean hasMemberMappings = memberMappings != null && !memberMappings.isEmpty(); + boolean hasMembers = !members.isEmpty(); + boolean hasMemberMappings = !memberMappings.isEmpty(); if (hasMembers && hasMemberMappings) { throw new ConsumerGroupException("either members or member mappings must be provided, not both"); @@ -118,7 +152,7 @@ public void validate() throws ConsumerGroupException { // Validate member mappings if (hasMemberMappings) { - if (memberMappings.size() < 1 || memberMappings.size() > maxMembers) { + if (memberMappings.size() > maxMembers) { throw new ConsumerGroupException("the number of member mappings must be between 1 and the max number of members"); } diff --git a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java index e4a9be3..b6c2a59 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java @@ -13,15 +13,17 @@ package io.synadia.pcg; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import io.nats.NatsRunnerUtils; +import io.nats.client.support.JsonParseException; import io.synadia.pcg.exceptions.ConsumerGroupException; import org.junit.jupiter.api.Test; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import static org.junit.jupiter.api.Assertions.*; @@ -31,7 +33,9 @@ */ class ElasticConsumerGroupTest { - private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + static { + NatsRunnerUtils.setDefaultOutputLevel(Level.SEVERE); + } @Test void testConfigBasic() { @@ -43,7 +47,7 @@ void testConfigBasic() { assertEquals(4, config.getMaxMembers()); assertEquals("foo.*", config.getFilter()); assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); - assertEquals(1000, config.getMaxBufferedMsgs()); + assertEquals(1000, config.getMaxBufferedMessages()); assertEquals(10000, config.getMaxBufferedBytes()); assertEquals(2, config.getMembers().size()); assertTrue(config.getMemberMappings().isEmpty()); @@ -229,13 +233,13 @@ void testGetPartitioningTransformDestPartialWildcards() { } @Test - void testJsonSerializationWithMembers() { + void testJsonSerializationWithMembers() throws JsonParseException { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( 4, "foo.*", new int[]{1}, 1000, 10000, Arrays.asList("m1", "m2"), new ArrayList<>() ); - String json = GSON.toJson(config); + String json = config.toJson(); // Verify JSON structure matches Go format assertTrue(json.contains("\"max_members\":4")); @@ -246,17 +250,17 @@ void testJsonSerializationWithMembers() { assertTrue(json.contains("\"members\":[\"m1\",\"m2\"]")); // Deserialize and verify - ElasticConsumerGroupConfig deserialized = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + ElasticConsumerGroupConfig deserialized = ElasticConsumerGroupConfig.instance(config.serialize()); assertEquals(config.getMaxMembers(), deserialized.getMaxMembers()); assertEquals(config.getFilter(), deserialized.getFilter()); assertArrayEquals(config.getPartitioningWildcards(), deserialized.getPartitioningWildcards()); - assertEquals(config.getMaxBufferedMsgs(), deserialized.getMaxBufferedMsgs()); + assertEquals(config.getMaxBufferedMessages(), deserialized.getMaxBufferedMessages()); assertEquals(config.getMaxBufferedBytes(), deserialized.getMaxBufferedBytes()); assertEquals(config.getMembers(), deserialized.getMembers()); } @Test - void testJsonSerializationWithMappings() { + void testJsonSerializationWithMappings() throws JsonParseException { List mappings = Arrays.asList( new MemberMapping("alice", new int[]{0, 1}), new MemberMapping("bob", new int[]{2, 3}) @@ -267,30 +271,30 @@ void testJsonSerializationWithMappings() { new ArrayList<>(), mappings ); - String json = GSON.toJson(config); + String json = config.toJson(); assertTrue(json.contains("\"member_mappings\"")); assertTrue(json.contains("\"member\":\"alice\"")); assertTrue(json.contains("\"partitions\":[0,1]")); // Deserialize and verify - ElasticConsumerGroupConfig deserialized = GSON.fromJson(json, ElasticConsumerGroupConfig.class); + ElasticConsumerGroupConfig deserialized = ElasticConsumerGroupConfig.instance(config.serialize()); assertEquals(2, deserialized.getMemberMappings().size()); assertEquals("alice", deserialized.getMemberMappings().get(0).getMember()); assertArrayEquals(new int[]{0, 1}, deserialized.getMemberMappings().get(0).getPartitions()); } @Test - void testJsonDeserializationFromGo() { + void testJsonDeserializationFromGo() throws JsonParseException { // This JSON is in the format produced by the Go implementation String goJson = "{\"max_members\":4,\"filter\":\"foo.*\",\"partitioning_wildcards\":[1],\"max_buffered_msg\":1000,\"max_buffered_bytes\":10000,\"members\":[\"m1\",\"m2\"]}"; - ElasticConsumerGroupConfig config = GSON.fromJson(goJson, ElasticConsumerGroupConfig.class); + ElasticConsumerGroupConfig config = ElasticConsumerGroupConfig.instance(goJson.getBytes(StandardCharsets.UTF_8)); assertEquals(4, config.getMaxMembers()); assertEquals("foo.*", config.getFilter()); assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); - assertEquals(1000, config.getMaxBufferedMsgs()); + assertEquals(1000, config.getMaxBufferedMessages()); assertEquals(10000, config.getMaxBufferedBytes()); assertEquals(2, config.getMembers().size()); assertEquals("m1", config.getMembers().get(0)); @@ -298,11 +302,11 @@ void testJsonDeserializationFromGo() { } @Test - void testJsonDeserializationWithMappingsFromGo() { + void testJsonDeserializationWithMappingsFromGo() throws JsonParseException { // This JSON is in the format produced by the Go implementation String goJson = "{\"max_members\":4,\"filter\":\"bar.*\",\"partitioning_wildcards\":[1],\"member_mappings\":[{\"member\":\"alice\",\"partitions\":[0,1]},{\"member\":\"bob\",\"partitions\":[2,3]}]}"; - ElasticConsumerGroupConfig config = GSON.fromJson(goJson, ElasticConsumerGroupConfig.class); + ElasticConsumerGroupConfig config = ElasticConsumerGroupConfig.instance(goJson.getBytes(StandardCharsets.UTF_8)); assertEquals(4, config.getMaxMembers()); assertEquals("bar.*", config.getFilter()); @@ -359,7 +363,7 @@ void testRevision() { assertEquals(42, config.getRevision()); // Verify revision is not serialized - String json = GSON.toJson(config); + String json = config.toJson(); assertFalse(json.contains("revision")); } diff --git a/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java index c48a3de..1faa142 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java @@ -13,9 +13,12 @@ package io.synadia.pcg; +import io.nats.NatsRunnerUtils; import io.nats.NatsServerRunner; import io.nats.client.Connection; +import io.nats.client.ErrorListener; import io.nats.client.Nats; +import io.nats.client.Options; import io.nats.client.api.ConsumerConfiguration; import io.nats.client.api.StorageType; import io.nats.client.api.StreamConfiguration; @@ -27,8 +30,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * Integration tests for Static and Elastic consumer groups. @@ -36,6 +41,10 @@ */ class IntegrationTest { + static { + NatsRunnerUtils.setDefaultOutputLevel(Level.SEVERE); + } + /** * Ported from Go TestStatic. * Creates a stream with subject transform, publishes 10 messages, @@ -44,7 +53,8 @@ class IntegrationTest { @Test void testStatic() throws Exception { try (NatsServerRunner server = NatsServerRunner.builder().jetstream(true).build()) { - Connection nc = Nats.connect(server.getNatsLocalhostUri()); + Options options = Options.builder().server(server.getNatsLocalhostUri()).errorListener(new ErrorListener() {}).build(); + Connection nc = Nats.connect(options); String streamName = "test"; String cgName = "group"; @@ -123,7 +133,8 @@ void testStatic() throws Exception { @Test void testElastic() throws Exception { try (NatsServerRunner server = NatsServerRunner.builder().jetstream(true).build()) { - Connection nc = Nats.connect(server.getNatsLocalhostUri()); + Options options = Options.builder().server(server.getNatsLocalhostUri()).errorListener(new ErrorListener() {}).build(); + Connection nc = Nats.connect(options); String streamName = "test"; String cgName = "group"; diff --git a/pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java b/pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java index fa65bc3..c11673c 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/PartitionUtilsTest.java @@ -13,14 +13,17 @@ package io.synadia.pcg; +import io.nats.NatsRunnerUtils; import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.logging.Level; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Unit tests for PartitionUtils. @@ -28,6 +31,10 @@ */ class PartitionUtilsTest { + static { + NatsRunnerUtils.setDefaultOutputLevel(Level.SEVERE); + } + @Test void testComposeKey() { assertEquals("mystream.mycg", PartitionUtils.composeKey("mystream", "mycg")); diff --git a/pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java b/pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java index dd8baeb..073d4ae 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/StaticConsumerGroupTest.java @@ -13,15 +13,17 @@ package io.synadia.pcg; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import io.nats.NatsRunnerUtils; +import io.nats.client.support.JsonParseException; import io.synadia.pcg.exceptions.ConsumerGroupException; import org.junit.jupiter.api.Test; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import static org.junit.jupiter.api.Assertions.*; @@ -31,7 +33,9 @@ */ class StaticConsumerGroupTest { - private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + static { + NatsRunnerUtils.setDefaultOutputLevel(Level.SEVERE); + } @Test void testConfigWithMembers() { @@ -185,14 +189,14 @@ void testValidationWithMembersSuccess() throws ConsumerGroupException { } @Test - void testJsonSerializationWithMembers() { + void testJsonSerializationWithMembers() throws JsonParseException { StaticConsumerGroupConfig config = new StaticConsumerGroupConfig( 4, "foo.>", Arrays.asList("m1", "m2"), new ArrayList<>() ); - String json = GSON.toJson(config); + String json = config.toJson(); // Verify JSON structure matches Go format assertTrue(json.contains("\"max_members\":4")); @@ -200,14 +204,14 @@ void testJsonSerializationWithMembers() { assertTrue(json.contains("\"members\":[\"m1\",\"m2\"]")); // Deserialize and verify - StaticConsumerGroupConfig deserialized = GSON.fromJson(json, StaticConsumerGroupConfig.class); + StaticConsumerGroupConfig deserialized = StaticConsumerGroupConfig.instance(json.getBytes(StandardCharsets.UTF_8)); assertEquals(config.getMaxMembers(), deserialized.getMaxMembers()); assertEquals(config.getFilter(), deserialized.getFilter()); assertEquals(config.getMembers(), deserialized.getMembers()); } @Test - void testJsonSerializationWithMappings() { + void testJsonSerializationWithMappings() throws JsonParseException { List mappings = Arrays.asList( new MemberMapping("alice", new int[]{0, 1}), new MemberMapping("bob", new int[]{2, 3}) @@ -217,7 +221,7 @@ void testJsonSerializationWithMappings() { 4, "foo.>", new ArrayList<>(), mappings ); - String json = GSON.toJson(config); + String json = config.toJson(); // Verify JSON structure matches Go format assertTrue(json.contains("\"max_members\":4")); @@ -226,7 +230,7 @@ void testJsonSerializationWithMappings() { assertTrue(json.contains("\"partitions\":[0,1]")); // Deserialize and verify - StaticConsumerGroupConfig deserialized = GSON.fromJson(json, StaticConsumerGroupConfig.class); + StaticConsumerGroupConfig deserialized = StaticConsumerGroupConfig.instance(json.getBytes(StandardCharsets.UTF_8)); assertEquals(config.getMaxMembers(), deserialized.getMaxMembers()); assertEquals(2, deserialized.getMemberMappings().size()); assertEquals("alice", deserialized.getMemberMappings().get(0).getMember()); @@ -234,11 +238,11 @@ void testJsonSerializationWithMappings() { } @Test - void testJsonDeserializationFromGo() { + void testJsonDeserializationFromGo() throws JsonParseException { // This JSON is in the format produced by the Go implementation String goJson = "{\"max_members\":4,\"filter\":\"foo.>\",\"members\":[\"m1\",\"m2\",\"m3\",\"m4\"]}"; - StaticConsumerGroupConfig config = GSON.fromJson(goJson, StaticConsumerGroupConfig.class); + StaticConsumerGroupConfig config = StaticConsumerGroupConfig.instance(goJson.getBytes(StandardCharsets.UTF_8)); assertEquals(4, config.getMaxMembers()); assertEquals("foo.>", config.getFilter()); @@ -247,11 +251,11 @@ void testJsonDeserializationFromGo() { } @Test - void testJsonDeserializationWithMappingsFromGo() { + void testJsonDeserializationWithMappingsFromGo() throws JsonParseException { // This JSON is in the format produced by the Go implementation String goJson = "{\"max_members\":4,\"filter\":\"bar.>\",\"member_mappings\":[{\"member\":\"alice\",\"partitions\":[0,1]},{\"member\":\"bob\",\"partitions\":[2,3]}]}"; - StaticConsumerGroupConfig config = GSON.fromJson(goJson, StaticConsumerGroupConfig.class); + StaticConsumerGroupConfig config = StaticConsumerGroupConfig.instance(goJson.getBytes(StandardCharsets.UTF_8)); assertEquals(4, config.getMaxMembers()); assertEquals("bar.>", config.getFilter()); From db7a4f4ee951185cf43d4be8f77873cf0ac90bab Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 23 Feb 2026 12:51:21 -0500 Subject: [PATCH 103/135] Remove GSON, use internal Json tool --- .github/workflows/pcg-main.yml | 2 +- .github/workflows/pcg-pr.yml | 2 +- .github/workflows/pcg-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pcg-main.yml b/.github/workflows/pcg-main.yml index 9f30756..0292b94 100644 --- a/.github/workflows/pcg-main.yml +++ b/.github/workflows/pcg-main.yml @@ -9,7 +9,7 @@ on: jobs: build: - uses: ./.github/workflows/workflow-main.yml + uses: synadia-io/workflows/.github/workflows/java-standard-main.yml@main with: project-dir: pcgroups secrets: inherit diff --git a/.github/workflows/pcg-pr.yml b/.github/workflows/pcg-pr.yml index 0d2c8ff..8032301 100644 --- a/.github/workflows/pcg-pr.yml +++ b/.github/workflows/pcg-pr.yml @@ -8,7 +8,7 @@ on: jobs: build: - uses: ./.github/workflows/workflow-pr.yml + uses: synadia-io/workflows/.github/workflows/java-standard-pr.yml@main with: project-dir: pcgroups secrets: inherit diff --git a/.github/workflows/pcg-release.yml b/.github/workflows/pcg-release.yml index 2f64eda..97ea73f 100644 --- a/.github/workflows/pcg-release.yml +++ b/.github/workflows/pcg-release.yml @@ -6,7 +6,7 @@ on: jobs: build: - uses: ./.github/workflows/workflow-release.yml + uses: synadia-io/workflows/.github/workflows/java-standard-release.yml@main with: project-dir: pcgroups secrets: inherit From bac90d049e9f34bafa86781fbe167899c008362b Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 23 Feb 2026 12:54:27 -0500 Subject: [PATCH 104/135] Readme, License and Notice --- pcgroups/LICENSE | 201 +++++++++++++++++++++++++++++++++++++++++++++ pcgroups/NOTICE | 5 ++ pcgroups/README.md | 11 +-- 3 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 pcgroups/LICENSE create mode 100644 pcgroups/NOTICE diff --git a/pcgroups/LICENSE b/pcgroups/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/pcgroups/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/pcgroups/NOTICE b/pcgroups/NOTICE new file mode 100644 index 0000000..ff3c8b4 --- /dev/null +++ b/pcgroups/NOTICE @@ -0,0 +1,5 @@ +Orbit Java +Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. + +This product includes software developed at +Synadia Communications Inc. \ No newline at end of file diff --git a/pcgroups/README.md b/pcgroups/README.md index 8eba332..331021d 100644 --- a/pcgroups/README.md +++ b/pcgroups/README.md @@ -1,9 +1,6 @@ -# Partitioned Consumer Groups - -[License-Url]: https://www.apache.org/licenses/LICENSE-2.0 -[License-Image]: https://img.shields.io/badge/License-Apache2-blue.svg +Orbit -[![License][License-Image]][License-Url] +# Partitioned Consumer Groups Initial implementation of a client-side partitioned consumer group feature for NATS streams leveraging some of the new features introduced in `nats-server` version 2.11. @@ -101,3 +98,7 @@ You can look at the `cg` CLI tool's source code for examples of how to create an # Requirements Partitioned consumer groups require NATS server version 2.11 or above. + +--- +Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. From ef4b3cc5f5f229d87e066b28cfa31089ef793ef1 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 23 Feb 2026 13:14:36 -0500 Subject: [PATCH 105/135] Address review --- pcgroups/NOTICE | 2 +- .../io/synadia/pcg/ElasticConsumerGroupConfig.java | 14 +++++++------- .../main/java/io/synadia/pcg/MemberMapping.java | 2 +- .../java/io/synadia/pcg/StaticConsumerGroup.java | 1 - .../io/synadia/pcg/StaticConsumerGroupConfig.java | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pcgroups/NOTICE b/pcgroups/NOTICE index ff3c8b4..bd2b8ad 100644 --- a/pcgroups/NOTICE +++ b/pcgroups/NOTICE @@ -2,4 +2,4 @@ Orbit Java Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. This product includes software developed at -Synadia Communications Inc. \ No newline at end of file +Synadia Communications Inc. diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java index 579001f..8c66dc2 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java @@ -288,17 +288,17 @@ public String getPartitioningTransformDest() { } @Override + @NonNull public String toJson() { StringBuilder sb = beginJson(); addField(sb, MAX_MEMBERS, maxMembers); addField(sb, FILTER, filter); - if (partitioningWildcards != null && partitioningWildcards.length > 0) { - sb.append("\"").append(PARTITIONING_WILDCARDS).append("\":["); - for (int i = 0; i < partitioningWildcards.length; i++) { - if (i > 0) sb.append(","); - sb.append(partitioningWildcards[i]); + if (partitioningWildcards.length > 0) { + List integers = new ArrayList<>(partitioningWildcards.length); + for (int i : partitioningWildcards) { + integers.add(i); } - sb.append("],"); + _addList(sb, PARTITIONING_WILDCARDS, integers, StringBuilder::append); } addField(sb, MAX_BUFFERED_MSG, maxBufferedMessages); addField(sb, MAX_BUFFERED_BYTES, maxBufferedBytes); @@ -334,7 +334,7 @@ public String toString() { "maxMembers=" + maxMembers + ", filter='" + filter + '\'' + ", partitioningWildcards=" + Arrays.toString(partitioningWildcards) + - ", maxBufferedMsgs=" + maxBufferedMessages + + ", maxBufferedMessages=" + maxBufferedMessages + ", maxBufferedBytes=" + maxBufferedBytes + ", members=" + members + ", memberMappings=" + memberMappings + diff --git a/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java b/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java index c81d3a4..e3b6652 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java +++ b/pcgroups/src/main/java/io/synadia/pcg/MemberMapping.java @@ -65,7 +65,7 @@ public String toJson() { StringBuilder sb = beginJson(); addField(sb, MEMBER, member); if (partitions.length > 0) { - List integers = new ArrayList(partitions.length); + List integers = new ArrayList<>(partitions.length); for (int i : partitions) { integers.add(i); } diff --git a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java index e9583b7..099e7b5 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java @@ -366,7 +366,6 @@ private void joinMemberConsumer() throws IOException, JetStreamApiException, Int // Create the durable consumer explicitly (matching Go's js.CreateConsumer) JetStreamManagement jsm = nc.jetStreamManagement(); -// System.out.printf("Creating consumer %s with filters %s and priority group %s%n\n", consumerName, filters, PRIORITY_GROUP_NAME); jsm.createConsumer(streamName, cc); // Get consumer context and start consuming diff --git a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java index dc0c885..da46472 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java +++ b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroupConfig.java @@ -114,7 +114,7 @@ public List getMemberMappings() { } public void setMemberMappings(List memberMappings) { - this.memberMappings = new ArrayList<>(memberMappings); + this.memberMappings = memberMappings == null ? new ArrayList<>() : new ArrayList<>(memberMappings); } /** From cde892a0a8c07d133aa194e5838a9849ded420a4 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 23 Feb 2026 13:39:55 -0500 Subject: [PATCH 106/135] No msgs except in cli --- .../java/io/synadia/pcg/ElasticConsumerGroup.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java index 09d5042..a798323 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java @@ -52,17 +52,17 @@ private ElasticConsumerGroup() { * @param maxMembers Maximum number of members (partitions) * @param filter Subject filter with wildcards * @param partitioningWildcards Indexes of wildcards to use for partitioning - * @param maxBufferedMsgs Max messages in work queue (0 for unlimited) + * @param maxBufferedMessages Max messages in work queue (0 for unlimited) * @param maxBufferedBytes Max bytes in work queue (0 for unlimited) * @return The created configuration */ public static ElasticConsumerGroupConfig create(Connection nc, String streamName, String consumerGroupName, int maxMembers, String filter, int[] partitioningWildcards, - long maxBufferedMsgs, long maxBufferedBytes) + long maxBufferedMessages, long maxBufferedBytes) throws ConsumerGroupException, IOException, JetStreamApiException, InterruptedException { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - maxMembers, filter, partitioningWildcards, maxBufferedMsgs, maxBufferedBytes, + maxMembers, filter, partitioningWildcards, maxBufferedMessages, maxBufferedBytes, new ArrayList<>(), new ArrayList<>()); config.validate(); @@ -96,7 +96,7 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName if (existingConfig != null) { if (existingConfig.getMaxMembers() != maxMembers || !Objects.equals(existingConfig.getFilter(), filter) || - existingConfig.getMaxBufferedMessages() != maxBufferedMsgs || + existingConfig.getMaxBufferedMessages() != maxBufferedMessages || existingConfig.getMaxBufferedBytes() != maxBufferedBytes || !Arrays.equals(existingConfig.getPartitioningWildcards(), partitioningWildcards)) { throw new ConsumerGroupException( @@ -121,8 +121,8 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName .discardPolicy(DiscardPolicy.New) .allowDirect(true); - if (maxBufferedMsgs > 0) { - scBuilder.maxMessages(maxBufferedMsgs); + if (maxBufferedMessages > 0) { + scBuilder.maxMessages(maxBufferedMessages); } if (maxBufferedBytes > 0) { scBuilder.maxBytes(maxBufferedBytes); From c5d08284c1cbc3f1bf1849e32e057a051b5f0ba8 Mon Sep 17 00:00:00 2001 From: scottf Date: Mon, 23 Feb 2026 13:44:19 -0500 Subject: [PATCH 107/135] javadoc file --- pcgroups/src/main/javadoc/images/favicon.ico | Bin 0 -> 1150 bytes pcgroups/src/main/javadoc/images/large-logo.png | Bin 0 -> 6533 bytes .../src/main/javadoc/images/synadia-logo.png | Bin 0 -> 19014 bytes pcgroups/src/main/javadoc/overview.html | 13 +++++++++++++ 4 files changed, 13 insertions(+) create mode 100644 pcgroups/src/main/javadoc/images/favicon.ico create mode 100644 pcgroups/src/main/javadoc/images/large-logo.png create mode 100644 pcgroups/src/main/javadoc/images/synadia-logo.png create mode 100644 pcgroups/src/main/javadoc/overview.html diff --git a/pcgroups/src/main/javadoc/images/favicon.ico b/pcgroups/src/main/javadoc/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9464855b4b53e64a0b464a138e21734ece9a878b GIT binary patch literal 1150 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 + + + + +NATS JetStream Partitioned Consumer Groups Library for Java +

    Synadia Logo

    + + + + From 67e692f05454eba10b1a3dfad631c75717e80a84 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 13:26:50 -0500 Subject: [PATCH 108/135] split pcgroups cli out into separate project --- .github/workflows/pcgcli-main.yml | 46 ++++ .github/workflows/pcgcli-pr.yml | 45 ++++ pcgroups-cli/build.gradle | 71 +++++ pcgroups-cli/gradle/libs.versions.toml | 12 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + pcgroups-cli/gradlew | 251 ++++++++++++++++++ pcgroups-cli/gradlew.bat | 94 +++++++ {pcgroups/cli => pcgroups-cli}/pom.xml | 18 +- pcgroups-cli/settings.gradle | 13 + .../java/io/synadia/pcg/cli/CgCommand.java | 0 .../java/io/synadia/pcg/cli/CliUtils.java | 0 .../io/synadia/pcg/cli/DurationConverter.java | 0 .../io/synadia/pcg/cli/ElasticCommands.java | 0 .../io/synadia/pcg/cli/PromptHandler.java | 0 .../io/synadia/pcg/cli/StaticCommands.java | 0 pcgroups/build.gradle | 18 +- 17 files changed, 546 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/pcgcli-main.yml create mode 100644 .github/workflows/pcgcli-pr.yml create mode 100644 pcgroups-cli/build.gradle create mode 100644 pcgroups-cli/gradle/libs.versions.toml create mode 100644 pcgroups-cli/gradle/wrapper/gradle-wrapper.jar create mode 100644 pcgroups-cli/gradle/wrapper/gradle-wrapper.properties create mode 100644 pcgroups-cli/gradlew create mode 100644 pcgroups-cli/gradlew.bat rename {pcgroups/cli => pcgroups-cli}/pom.xml (90%) create mode 100644 pcgroups-cli/settings.gradle rename {pcgroups/cli => pcgroups-cli}/src/main/java/io/synadia/pcg/cli/CgCommand.java (100%) rename {pcgroups/cli => pcgroups-cli}/src/main/java/io/synadia/pcg/cli/CliUtils.java (100%) rename {pcgroups/cli => pcgroups-cli}/src/main/java/io/synadia/pcg/cli/DurationConverter.java (100%) rename {pcgroups/cli => pcgroups-cli}/src/main/java/io/synadia/pcg/cli/ElasticCommands.java (100%) rename {pcgroups/cli => pcgroups-cli}/src/main/java/io/synadia/pcg/cli/PromptHandler.java (100%) rename {pcgroups/cli => pcgroups-cli}/src/main/java/io/synadia/pcg/cli/StaticCommands.java (100%) diff --git a/.github/workflows/pcgcli-main.yml b/.github/workflows/pcgcli-main.yml new file mode 100644 index 0000000..dfb6d30 --- /dev/null +++ b/.github/workflows/pcgcli-main.yml @@ -0,0 +1,46 @@ +name: Partitioned Consumer Groups Main Snapshot + +on: + push: + branches: + - main + paths: + - 'pcgroups-cli/**' + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./pcgroups-cli + steps: + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + - name: Check out code + uses: actions/checkout@v4 + - name: Build with Gradle + run: | + chmod +x gradlew && ./gradlew clean test uberJar + - name: Build with Maven + run: mvn clean package + - name: Validate Gradle Uber was created + run: | + FILE_PATH="build/libs/cg.jar" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi + - name: Validate Maven Uber was created + run: | + FILE_PATH="target/cg.jar" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi diff --git a/.github/workflows/pcgcli-pr.yml b/.github/workflows/pcgcli-pr.yml new file mode 100644 index 0000000..d5d243a --- /dev/null +++ b/.github/workflows/pcgcli-pr.yml @@ -0,0 +1,45 @@ +name: Partitioned Consumer Groups Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'pcgroups-cli/**' + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./pcgroups-cli + steps: + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + - name: Check out code + uses: actions/checkout@v4 + - name: Build with Gradle + run: | + chmod +x gradlew && ./gradlew clean test uberJar + - name: Build with Maven + run: mvn clean package + - name: Validate Gradle Uber was created + run: | + FILE_PATH="build/libs/cg.jar" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi + - name: Validate Maven Uber was created + run: | + FILE_PATH="target/cg.jar" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle new file mode 100644 index 0000000..93a4161 --- /dev/null +++ b/pcgroups-cli/build.gradle @@ -0,0 +1,71 @@ +plugins { + id("java") + id("java-library") + id("maven-publish") + id("jacoco") + id("biz.aQute.bnd.builder") version "7.1.0" + id("org.gradle.test-retry") version "1.6.4" + id("io.github.gradle-nexus.publish-plugin") version "2.0.0" + id("signing") +} + +group = 'io.synadia' +version = "0.1.0" +def originalUber = 'pcg-cli-' + version + '-uber.jar' +System.out.println(originalUber) + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() + mavenLocal() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots" } +} + +dependencies { + implementation 'io.nats:jnats:2.25.1' + implementation 'org.jspecify:jspecify:1.0.0' + implementation 'io.synadia:pcgroups:0.1.0-SNAPSHOT' + implementation 'info.picocli:picocli:4.7.5' + + testImplementation 'io.nats:jnats-server-runner:3.1.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' + testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' +} + +tasks.register('copyToLib', Copy) { + into "build/libs" + from configurations.runtimeClasspath +} + +tasks.register('packageUberJar', Jar) { + archiveClassifier = 'uber' + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } + } + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + dependsOn copyToLib +} + +tasks.register('uberJar', Copy) { + dependsOn packageUberJar + + // we want the file to be called cg.jar + from ('build/libs') + include originalUber + destinationDir file('build/libs/') + rename originalUber, "cg.jar" + + // this task actually does a copy, so this part removes the original + doLast { + delete('build/libs/' + originalUber) + } +} diff --git a/pcgroups-cli/gradle/libs.versions.toml b/pcgroups-cli/gradle/libs.versions.toml new file mode 100644 index 0000000..2cfe86a --- /dev/null +++ b/pcgroups-cli/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.4.5-jre" +junit-jupiter = "5.12.1" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/pcgroups-cli/gradle/wrapper/gradle-wrapper.jar b/pcgroups-cli/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..1b33c55baabb587c669f562ae36f953de2481846 GIT binary patch literal 43764 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/pcgroups-cli/gradlew.bat b/pcgroups-cli/gradlew.bat new file mode 100644 index 0000000..db3a6ac --- /dev/null +++ b/pcgroups-cli/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pcgroups/cli/pom.xml b/pcgroups-cli/pom.xml similarity index 90% rename from pcgroups/cli/pom.xml rename to pcgroups-cli/pom.xml index f31a38f..9091eaf 100644 --- a/pcgroups/cli/pom.xml +++ b/pcgroups-cli/pom.xml @@ -19,7 +19,7 @@ 4.0.0 io.synadia - cg-cli + pcg-cli 0.1.0 jar @@ -27,20 +27,19 @@ CLI tool for managing NATS JetStream Partitioned Consumer Groups - 11 - 11 + 1.8 UTF-8 + 0.1.0-SNAPSHOT 4.7.5 2.25.1 - 2.11.0 io.synadia - partitioned-consumer-groups - 0.1.0 + pcgroups + ${pcgroups.version} @@ -50,13 +49,6 @@ ${jnats.version} - - - com.google.code.gson - gson - ${gson.version} - - info.picocli diff --git a/pcgroups-cli/settings.gradle b/pcgroups-cli/settings.gradle new file mode 100644 index 0000000..ce87931 --- /dev/null +++ b/pcgroups-cli/settings.gradle @@ -0,0 +1,13 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://plugins.gradle.org/m2/" } + } + plugins { + id("biz.aQute.bnd.builder") version "7.1.0" + } +} +rootProject.name = 'pcg-cli' diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CgCommand.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/CgCommand.java similarity index 100% rename from pcgroups/cli/src/main/java/io/synadia/pcg/cli/CgCommand.java rename to pcgroups-cli/src/main/java/io/synadia/pcg/cli/CgCommand.java diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/CliUtils.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/CliUtils.java similarity index 100% rename from pcgroups/cli/src/main/java/io/synadia/pcg/cli/CliUtils.java rename to pcgroups-cli/src/main/java/io/synadia/pcg/cli/CliUtils.java diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java similarity index 100% rename from pcgroups/cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java rename to pcgroups-cli/src/main/java/io/synadia/pcg/cli/DurationConverter.java diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java similarity index 100% rename from pcgroups/cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java rename to pcgroups-cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java similarity index 100% rename from pcgroups/cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java rename to pcgroups-cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java diff --git a/pcgroups/cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java similarity index 100% rename from pcgroups/cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java rename to pcgroups-cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java diff --git a/pcgroups/build.gradle b/pcgroups/build.gradle index c84dda9..75f3332 100644 --- a/pcgroups/build.gradle +++ b/pcgroups/build.gradle @@ -35,28 +35,14 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.25.1' - implementation 'org.jspecify:jspecify:1.0.0' + api 'io.nats:jnats:2.25.1' + api 'org.jspecify:jspecify:1.0.0' testImplementation 'io.nats:jnats-server-runner:3.1.0' - testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } -sourceSets { - main { - java { - srcDirs = ['src/main/java'] - } - } - test { - java { - srcDirs = ['src/test/java'] - } - } -} - tasks.register('bundle', Bundle) { from sourceSets.main.output } From cd80d6a15baaa954883f3abe2a28e6b5958ac601 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 13:35:06 -0500 Subject: [PATCH 109/135] pcgroups cli work --- .github/workflows/pcgcli-main.yml | 2 +- .github/workflows/pcgcli-pr.yml | 2 +- pcgroups-cli/build.gradle | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pcgcli-main.yml b/.github/workflows/pcgcli-main.yml index dfb6d30..003b2e5 100644 --- a/.github/workflows/pcgcli-main.yml +++ b/.github/workflows/pcgcli-main.yml @@ -1,4 +1,4 @@ -name: Partitioned Consumer Groups Main Snapshot +name: Partitioned Consumer Groups CLI Main Snapshot on: push: diff --git a/.github/workflows/pcgcli-pr.yml b/.github/workflows/pcgcli-pr.yml index d5d243a..b91da6b 100644 --- a/.github/workflows/pcgcli-pr.yml +++ b/.github/workflows/pcgcli-pr.yml @@ -1,4 +1,4 @@ -name: Partitioned Consumer Groups Pull Request +name: Partitioned Consumer Groups CLI Pull Request on: pull_request: diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index 93a4161..91d173f 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -12,7 +12,6 @@ plugins { group = 'io.synadia' version = "0.1.0" def originalUber = 'pcg-cli-' + version + '-uber.jar' -System.out.println(originalUber) java { sourceCompatibility = JavaVersion.VERSION_1_8 From 6be08a99b3ceb17d508281019bc3b2d6c5d2bb1b Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 13:38:14 -0500 Subject: [PATCH 110/135] need snapshot repo --- pcgroups-cli/pom.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pcgroups-cli/pom.xml b/pcgroups-cli/pom.xml index 9091eaf..c5ff3c3 100644 --- a/pcgroups-cli/pom.xml +++ b/pcgroups-cli/pom.xml @@ -34,6 +34,23 @@ 2.25.1 + + + sonatype releases + https://repo1.maven.org/maven2/ + + true + + + + sonatype snapshots + https://central.sonatype.com/repository/maven-snapshots + + true + + + + From 3ab9bef04f4db2e2f2a31765893bb28c72e9cfc1 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 17:12:15 -0500 Subject: [PATCH 111/135] Update Readmes --- README.md | 105 ++++++++++++++++---------------- batch-publish/README.md | 13 ++-- chaos-runner/README.md | 13 ++-- counters/README.md | 12 ++-- direct-batch/README.md | 13 ++-- encoded-kv/README.md | 13 ++-- js-publish-extensions/README.md | 13 ++-- request-many/README.md | 13 ++-- retrier/README.md | 13 ++-- schedule-message/README.md | 15 ++--- 10 files changed, 104 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index e5d0bf0..0709bcf 100644 --- a/README.md +++ b/README.md @@ -22,122 +22,125 @@ client. Note that these libraries will evolve rapidly and API guarantees are gen ## Retrier -Extension for retrying anything. +Extension for retrying anything. -**Current Release**: 0.2.1 -  **Current Snapshot**: 0.2.2-SNAPSHOT +[Retrier README](retrier/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=retrier/README.md)](retrier/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:retrier-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:retrier-197556?labelColor=grey&style=flat) +![0.2.1](https://img.shields.io/badge/Current_Release-0.2.1-27AAE0) +![0.2.2](https://img.shields.io/badge/Current_Snapshot-0.2.2--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/retrier/javadoc.svg)](https://javadoc.io/doc/io.synadia/retrier) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/retrier)](https://img.shields.io/maven-central/v/io.synadia/retrier) ## Jetstream Publish Extensions General extensions for Jetstream Publishing -**Current Release**: 0.4.4 -  **Current Snapshot**: 0.4.5-SNAPSHOT +[Jetstream Publish Extensions README](js-publish-extensions/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=js-publish-extensions/README.md)](js-publish-extensions/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:jnats--js--publish--extensions-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:jnats--js--publish--extensions-197556?labelColor=grey&style=flat) +![0.4.4](https://img.shields.io/badge/Current_Release-0.4.4-27AAE0) +![0.4.5](https://img.shields.io/badge/Current_Snapshot-0.4.5--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/jnats-js-publish-extensions/javadoc.svg)](https://javadoc.io/doc/io.synadia/jnats-js-publish-extensions) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/jnats-js-publish-extensions/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/jnats-js-publish-extensions) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/jnats-js-publish-extensions)](https://img.shields.io/maven-central/v/io.synadia/jnats-js-publish-extensions) ## Request Many Extension to get many responses for a single core request. -**Current Release**: 0.1.1 -  **Current Snapshot**: 0.1.2-SNAPSHOT +[Request Many README](request-many/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=request-many/README.md)](request-many/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:request--many-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:request--many-197556?labelColor=grey&style=flat) +![0.1.1](https://img.shields.io/badge/Current_Release-0.1.1-27AAE0) +![0.1.2](https://img.shields.io/badge/Current_Snapshot-0.1.2--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/request-many/javadoc.svg)](https://javadoc.io/doc/io.synadia/request-many) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/request-many/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/request-many) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/request-many)](https://img.shields.io/maven-central/v/io.synadia/request-many) ## Encoded KeyValue Extension over Key Value to allow custom encoding of keys and values. -**Current Release**: 0.0.4 -  **Current Snapshot**: 0.0.5-SNAPSHOT +[Encoded KeyValue README](encoded-kv/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=encoded-kv/README.md)](encoded-kv/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-197556?labelColor=grey&style=flat) +![0.0.4](https://img.shields.io/badge/Current_Release-0.0.4-27AAE0) +![0.0.5](https://img.shields.io/badge/Current_Snapshot-0.0.5--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/encoded-kv/javadoc.svg)](https://javadoc.io/doc/io.synadia/encoded-kv) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/encoded-kv/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/encoded-kv) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/encoded-kv)](https://img.shields.io/maven-central/v/io.synadia/encoded-kv) ## Direct Batch The direct batch functionality leverages the direct message capabilities introduced in NATS Server v2.11. The functionality is described in [ADR-31](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-31.md) -**Current Release**: 0.1.4 - **Current Snapshot**: 0.1.5-SNAPSHOT +[Direct Batch README](direct-batch/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=direct-batch/README.md)](direct-batch/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:direct--batch-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:direct--batch-197556?labelColor=grey&style=flat) +![0.1.4](https://img.shields.io/badge/Current_Release-0.1.4-27AAE0) +![0.1.5](https://img.shields.io/badge/Current_Snapshot-0.1.5--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/direct-batch/javadoc.svg)](https://javadoc.io/doc/io.synadia/direct-batch) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/direct-batch/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/direct-batch) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/direct-batch)](https://img.shields.io/maven-central/v/io.synadia/direct-batch) ### Batch Publish Utility to publish an atomic batch, a group of up to 1000 messages -**Current Release**: 0.2.2 - **Current Snapshot**: 0.2.3-SNAPSHOT +[Batch Publish README](batch-publish/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=batch-publish/README.md)](batch-publish/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-197556?labelColor=grey&style=flat) +![0.2.2](https://img.shields.io/badge/Current_Release-0.2.2-27AAE0) +![0.2.3](https://img.shields.io/badge/Current_Snapshot-0.2.3--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/batch-publish/javadoc.svg)](https://javadoc.io/doc/io.synadia/batch-publish) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/batch-publish)](https://img.shields.io/maven-central/v/io.synadia/batch-publish) ### Distributed Counters Utility to take leverage the distributed counter functionality. -**Current Release**: 0.2.2 -  **Current Snapshot**: 0.2.3-SNAPSHOT +[Distributed Counters README](counters/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=counters/README.md)](counters/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-197556?labelColor=grey&style=flat) +![0.2.2](https://img.shields.io/badge/Current_Release-0.2.2-27AAE0) +![0.2.3](https://img.shields.io/badge/Current_Snapshot-0.2.3--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/counters/javadoc.svg)](https://javadoc.io/doc/io.synadia/counters) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/counters)](https://img.shields.io/maven-central/v/io.synadia/counters) -### Scheduled Message +### Schedule Message Utility to leverage the ability to schedule a message to be published at a later time. Eventually the ability to schedule a message to publish based on a cron or schedule. -**Current Release**: 0.0.3 -  **Current Snapshot**: 0.0.4-SNAPSHOT +[Scheduled Message README](schedule-message/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=schedule-message/README.md)](schedule-message/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) -[![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:schedule--message-197556?labelColor=grey&style=flat) +![0.0.3](https://img.shields.io/badge/Current_Release-0.0.3-27AAE0) +![0.0.4](https://img.shields.io/badge/Current_Snapshot-0.0.4--SNAPSHOT-27AAE0) +[![javadoc](https://javadoc.io/badge2/io.synadia/schedule-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/schedule-message) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/schedule-message)](https://img.shields.io/maven-central/v/io.synadia/schedule-message) ## Chaos Runner Run some NATS servers and cause chaos by bringing them up and down. -**Current Release**: 0.0.8 -  **Current Snapshot**: 0.0.9-SNAPSHOT +[Chaos Runner README](chaos-runner/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](chaos-runner/README.md) -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-197556?labelColor=grey&style=flat) +![0.0.8](https://img.shields.io/badge/Current_Release-0.0.8-27AAE0) +![0.0.9](https://img.shields.io/badge/Current_Snapshot-0.0.9--SNAPSHOT-27AAE0) [![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/chaos-runner)](https://img.shields.io/maven-central/v/io.synadia/chaos-runner) ## Partitioned Consumer Groups Implementation of the partitioned Consumer Group functionality, ported from and compatible with the [Golang version](https://github.com/synadia-io/orbit.go/tree/main/pcgroups). -**Current Release**: 0.0.1 -  **Current Snapshot**: 0.0.1-SNAPSHOT +[Partitioned Consumer Groups README](pcgroups/README.md) -[![README](https://img.shields.io/badge/README-blue?style=flat&link=chaos-runner/README.md)](pcgroups/README.md) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:pcgroups-197556?labelColor=grey&style=flat) +![0.1.0](https://img.shields.io/badge/Current_Release-0.1.0-27AAE0) +![0.1.1](https://img.shields.io/badge/Current_Snapshot-0.1.1--SNAPSHOT-27AAE0) +[![javadoc](https://javadoc.io/badge2/io.synadia/pcgroups/javadoc.svg)](https://javadoc.io/doc/io.synadia/pcgroups) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/pcgroups)](https://img.shields.io/maven-central/v/io.synadia/pcgroups) # Dependencies ### Gradle diff --git a/batch-publish/README.md b/batch-publish/README.md index dc338bd..945ba3c 100644 --- a/batch-publish/README.md +++ b/batch-publish/README.md @@ -11,15 +11,12 @@ Utility to publish an atomic batch, a group of up to 1000 messages https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-50.md -**Current Release**: 0.2.2 - **Current Snapshot**: 0.2.3-SNAPSHOT -  **Gradle and Maven** `io.synadia:batch-publish` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) - -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-00BC8E?labelColor=grey&style=flat) -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/batch-publish) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:batch--publish-197556?labelColor=grey&style=flat) +![0.2.2](https://img.shields.io/badge/Current_Release-0.2.2-27AAE0) +![0.2.3](https://img.shields.io/badge/Current_Snapshot-0.2.3--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) [![javadoc](https://javadoc.io/badge2/io.synadia/batch-publish/javadoc.svg)](https://javadoc.io/doc/io.synadia/batch-publish) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/batch-publish)](https://img.shields.io/maven-central/v/io.synadia/batch-publish) --- diff --git a/chaos-runner/README.md b/chaos-runner/README.md index 099d146..dec33c7 100644 --- a/chaos-runner/README.md +++ b/chaos-runner/README.md @@ -5,11 +5,12 @@ A simple java program that can start 1 or more NATS Servers and then add chaos, by taking one of them down on a delay and bringing it back up after a downtime. -**Current Release**: 0.0.8 -  **Current Snapshot**: 0.0.9-SNAPSHOT -  **Gradle and Maven** `io.synadia:chaos-runner` - -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-197556?labelColor=grey&style=flat) +![0.0.8](https://img.shields.io/badge/Current_Release-0.0.8-27AAE0) +![0.0.9](https://img.shields.io/badge/Current_Snapshot-0.0.9--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) +[![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/chaos-runner)](https://img.shields.io/maven-central/v/io.synadia/chaos-runner) ## Uber Jar @@ -48,7 +49,7 @@ So for example, for 3 servers... * If the starting server port is 4222, the other ports are 4223 and 4224. * If the listen port is 4232, the other listen ports are 4232 and 4233 -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:chaos--runner-197556?labelColor=grey&style=flat) [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/chaos-runner) [![javadoc](https://javadoc.io/badge2/io.synadia/chaos-runner/javadoc.svg)](https://javadoc.io/doc/io.synadia/chaos-runner) diff --git a/counters/README.md b/counters/README.md index d278513..a292e3d 100644 --- a/counters/README.md +++ b/counters/README.md @@ -6,10 +6,12 @@ Utility to take advantage of the distributed counter functionality. https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md -**Current Release**: 0.2.2 -  **Current Snapshot**: 0.2.3-SNAPSHOT -  **Gradle and Maven** `io.synadia:counters` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-197556?labelColor=grey&style=flat) +![0.2.2](https://img.shields.io/badge/Current_Release-0.2.2-27AAE0) +![0.2.3](https://img.shields.io/badge/Current_Snapshot-0.2.3--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) +[![javadoc](https://javadoc.io/badge2/io.synadia/counters/javadoc.svg)](https://javadoc.io/doc/io.synadia/counters) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/counters)](https://img.shields.io/maven-central/v/io.synadia/counters) ## Basic Usage @@ -78,7 +80,7 @@ public CounterIterator iterateEntries(String... subjects) public CounterIterator iterateEntries(List subjects) public CounterIterator iterateEntries(List subjects, Duration timeoutFirst, Duration timeoutSubsequent) ``` -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-00BC8E?labelColor=grey&style=flat) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counters-197556?labelColor=grey&style=flat) [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counters) [![javadoc](https://javadoc.io/badge2/io.synadia/counters/javadoc.svg)](https://javadoc.io/doc/io.synadia/counters) diff --git a/direct-batch/README.md b/direct-batch/README.md index 424c834..4cf9f27 100644 --- a/direct-batch/README.md +++ b/direct-batch/README.md @@ -8,15 +8,12 @@ It only works with the 2.11.x NATS Server and the JNats 2.20.5.main-2-11-SNAPSHO The direct batch functionality leverages the direct message capabilities introduced in NATS Server 2.11 The functionality is described in [ADR-31](https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-31.md) -**Current Release**: 0.1.4 - **Current Snapshot**: 0.1.5-SNAPSHOT -  **Gradle and Maven** `io.synadia:direct-batch` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) - -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:direct--batch-00BC8E?labelColor=grey&style=flat) -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/direct-batch/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/direct-batch) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:direct--batch-197556?labelColor=grey&style=flat) +![0.1.4](https://img.shields.io/badge/Current_Release-0.1.4-27AAE0) +![0.1.5](https://img.shields.io/badge/Current_Snapshot-0.1.5--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) [![javadoc](https://javadoc.io/badge2/io.synadia/direct-batch/javadoc.svg)](https://javadoc.io/doc/io.synadia/direct-batch) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/direct-batch)](https://img.shields.io/maven-central/v/io.synadia/direct-batch) ## Message Info diff --git a/encoded-kv/README.md b/encoded-kv/README.md index dbb59d1..d4d2961 100644 --- a/encoded-kv/README.md +++ b/encoded-kv/README.md @@ -14,15 +14,12 @@ It requires a _codec_, which * decodes the encoded key back to the key object * decodes the encoded data bytes back into the value object. -**Current Release**: 0.0.4 -  **Current Snapshot**: 0.0.5-SNAPSHOT -  **Gradle and Maven** `io.synadia:encoded-kv` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) - -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-00BC8E?labelColor=grey&style=flat) -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/encoded-kv/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/encoded-kv) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:encoded--kv-197556?labelColor=grey&style=flat) +![0.0.4](https://img.shields.io/badge/Current_Release-0.0.4-27AAE0) +![0.0.5](https://img.shields.io/badge/Current_Snapshot-0.0.5--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) [![javadoc](https://javadoc.io/badge2/io.synadia/encoded-kv/javadoc.svg)](https://javadoc.io/doc/io.synadia/encoded-kv) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/encoded-kv)](https://img.shields.io/maven-central/v/io.synadia/encoded-kv) --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. diff --git a/js-publish-extensions/README.md b/js-publish-extensions/README.md index a6763d7..e8e9909 100644 --- a/js-publish-extensions/README.md +++ b/js-publish-extensions/README.md @@ -4,15 +4,12 @@ Extensions specific to JetStream publishing. -**Current Release**: 0.4.4 -  **Current Snapshot**: 0.4.5-SNAPSHOT -  **Gradle and Maven** `io.synadia:jnats-js-publish-extensions` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) - -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:jnats--js--publish--extensions-00BC8E?labelColor=grey&style=flat) -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/jnats-js-publish-extensions/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/jnats-js-publish-extensions) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:jnats--js--publish--extensions-197556?labelColor=grey&style=flat) +![0.4.4](https://img.shields.io/badge/Current_Release-0.4.4-27AAE0) +![0.4.5](https://img.shields.io/badge/Current_Snapshot-0.4.5--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) [![javadoc](https://javadoc.io/badge2/io.synadia/jnats-js-publish-extensions/javadoc.svg)](https://javadoc.io/doc/io.synadia/jnats-js-publish-extensions) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/jnats-js-publish-extensions)](https://img.shields.io/maven-central/v/io.synadia/jnats-js-publish-extensions) ### PublishRetrier diff --git a/request-many/README.md b/request-many/README.md index fd4519b..91f2815 100644 --- a/request-many/README.md +++ b/request-many/README.md @@ -9,15 +9,12 @@ instead of the usual first response for a request. This allows you to implement * Scatter-gather pattern, getting responses from many workers. * Many responses, for instance, a multipart payload, from a single worker. -**Current Release**: 0.1.1 -  **Current Snapshot**: 0.1.2-SNAPSHOT -  **Gradle and Maven** `io.synadia:request-many` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) - -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:request--many-00BC8E?labelColor=grey&style=flat) -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/request-many/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/request-many) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:request--many-197556?labelColor=grey&style=flat) +![0.1.1](https://img.shields.io/badge/Current_Release-0.1.1-27AAE0) +![0.1.2](https://img.shields.io/badge/Current_Snapshot-0.1.2--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) [![javadoc](https://javadoc.io/badge2/io.synadia/request-many/javadoc.svg)](https://javadoc.io/doc/io.synadia/request-many) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/request-many)](https://img.shields.io/maven-central/v/io.synadia/request-many) ### Request Many Usage diff --git a/retrier/README.md b/retrier/README.md index 3633313..6ab93bc 100644 --- a/retrier/README.md +++ b/retrier/README.md @@ -4,15 +4,12 @@ The retrier is a general purpose Java utility that can execute any action (function) in a retriable manner. -**Current Release**: 0.2.1 -  **Current Snapshot**: 0.2.2-SNAPSHOT -  **Gradle and Maven** `io.synadia:retrier` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) - -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:retrier-00BC8E?labelColor=grey&style=flat) -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/retrier) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:retrier-197556?labelColor=grey&style=flat) +![0.2.1](https://img.shields.io/badge/Current_Release-0.2.1-27AAE0) +![0.2.2](https://img.shields.io/badge/Current_Snapshot-0.2.2--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) [![javadoc](https://javadoc.io/badge2/io.synadia/retrier/javadoc.svg)](https://javadoc.io/doc/io.synadia/retrier) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/retrier)](https://img.shields.io/maven-central/v/io.synadia/retrier) ### Retrier Usage diff --git a/schedule-message/README.md b/schedule-message/README.md index dfb7953..90c7fba 100644 --- a/schedule-message/README.md +++ b/schedule-message/README.md @@ -7,15 +7,12 @@ Eventually the ability to schedule a message to publish based on a cron or sched https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-51.md -**Current Release**: 0.0.3 -  **Current Snapshot**: 0.0.4-SNAPSHOT -  **Gradle and Maven** `io.synadia:scheduled-message` -[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) - -![Artifact](https://img.shields.io/badge/Artifact-io.synadia:scheduled--message-00BC8E?labelColor=grey&style=flat) -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/scheduled-message) -[![javadoc](https://javadoc.io/badge2/io.synadia/scheduled-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/scheduled-message) +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:schedule--message-197556?labelColor=grey&style=flat) +![0.0.3](https://img.shields.io/badge/Current_Release-0.0.3-27AAE0) +![0.0.4](https://img.shields.io/badge/Current_Snapshot-0.0.4--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) +[![javadoc](https://javadoc.io/badge2/io.synadia/schedule-message/javadoc.svg)](https://javadoc.io/doc/io.synadia/schedule-message) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/schedule-message)](https://img.shields.io/maven-central/v/io.synadia/schedule-message) ### Building a Scheduled Message From 7c9ba0281e70918e41fdeea5c4617fbb74c4555c Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 17:12:58 -0500 Subject: [PATCH 112/135] Update Readmes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0709bcf..c7f48fa 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ client. Note that these libraries will evolve rapidly and API guarantees are gen | Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | | Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | | Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | -| Batch Publish | Publish an atomic batch | [README.md](batch-publish/README.md) | 0.0.0 | 0.0.0-SNAPSHOT | +| Batch Publish | Publish an atomic batch | [README.md](batch-publish/README.md) | 0.2.2 | 0.2.3-SNAPSHOT | | Distributed Counters | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.2.2 | 0.2.3-SNAPSHOT | | Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.3 | 0.0.4-SNAPSHOT | | Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | -| Partitioned Consumer Groups | Partitioned Consumer Group funcitionality for JetStream | [README.md](pcgroups/README.md) | 0.0.1 | 0.0.1-SNAPSHOT | +| Partitioned Consumer Groups | Partitioned Consumer Group funcitionality for JetStream | [README.md](pcgroups/README.md) | 0.1.1 | 0.1.1-SNAPSHOT | ## Retrier From c18d76eaa841571bbe92977b70a95c7fe7255fb2 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 17:14:21 -0500 Subject: [PATCH 113/135] PCG-CLI build and readme --- .github/workflows/pcgcli-main.yml | 4 +- .github/workflows/pcgcli-pr.yml | 20 ++++++-- pcgroups-cli/README.md | 82 +++++++++++++++++++++++++++++++ pcgroups-cli/build.gradle | 62 +++++++++++------------ pcgroups/README.md | 11 +++++ pcgroups/build.gradle | 2 +- 6 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 pcgroups-cli/README.md diff --git a/.github/workflows/pcgcli-main.yml b/.github/workflows/pcgcli-main.yml index 003b2e5..3c2c42d 100644 --- a/.github/workflows/pcgcli-main.yml +++ b/.github/workflows/pcgcli-main.yml @@ -26,7 +26,7 @@ jobs: chmod +x gradlew && ./gradlew clean test uberJar - name: Build with Maven run: mvn clean package - - name: Validate Gradle Uber was created + - name: Validate Gradle Executable Jar was created run: | FILE_PATH="build/libs/cg.jar" if [ -f "$FILE_PATH" ]; then @@ -35,7 +35,7 @@ jobs: echo "Validation failed: $FILE_PATH was not found." exit 1 # Fails the workflow step fi - - name: Validate Maven Uber was created + - name: Validate Maven Executable Jar was created run: | FILE_PATH="target/cg.jar" if [ -f "$FILE_PATH" ]; then diff --git a/.github/workflows/pcgcli-pr.yml b/.github/workflows/pcgcli-pr.yml index b91da6b..250888d 100644 --- a/.github/workflows/pcgcli-pr.yml +++ b/.github/workflows/pcgcli-pr.yml @@ -22,10 +22,10 @@ jobs: uses: actions/checkout@v4 - name: Build with Gradle run: | - chmod +x gradlew && ./gradlew clean test uberJar + chmod +x gradlew && ./gradlew clean package - name: Build with Maven run: mvn clean package - - name: Validate Gradle Uber was created + - name: Validate artifacts were created run: | FILE_PATH="build/libs/cg.jar" if [ -f "$FILE_PATH" ]; then @@ -34,8 +34,20 @@ jobs: echo "Validation failed: $FILE_PATH was not found." exit 1 # Fails the workflow step fi - - name: Validate Maven Uber was created - run: | + FILE_PATH="build/cg.zip" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi + FILE_PATH="build/libs/cg.tar" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi FILE_PATH="target/cg.jar" if [ -f "$FILE_PATH" ]; then echo "Validation successful: $FILE_PATH was created." diff --git a/pcgroups-cli/README.md b/pcgroups-cli/README.md new file mode 100644 index 0000000..52014c5 --- /dev/null +++ b/pcgroups-cli/README.md @@ -0,0 +1,82 @@ +Orbit + +# Partitioned Consumer Groups CLI + +The Partitioned Consumer Groups CLI is a command line tool. In the usage, + +`cg` stands for `java -jar /cg.jar` + +``` +Usage: cg [options] + +Commands: + static Static consumer groups mode + elastic Elastic consumer groups mode + +Use 'cg --help' for more information about a command. +``` + +``` +Usage: cg static [COMMAND] +Static consumer groups mode +Commands: + ls, list List static consumer groups for a stream + info Get static consumer group info + create Create a static partitioned consumer group + delete, rm Delete a static partitioned consumer group + member-info, memberinfo, minfo Get static consumer group member info + step-down, stepdown, sd Initiate a step down for a member + consume, join Join a static partitioned consumer group + prompt Interactive prompt mode +``` + +``` +Usage: cg elastic [COMMAND] +Elastic consumer groups mode +Commands: + ls, list List elastic consumer groups for a stream + info Get elastic consumer group info + create Create an elastic partitioned consumer + group + delete, rm Delete an elastic partitioned consumer + group + add Add members to an elastic consumer group + drop Drop members from an elastic consumer group + create-mapping, cm, createmapping Create member mappings for an elastic + consumer group + delete-mapping, dm, deletemapping Delete member mappings for an elastic + consumer group + member-info, memberinfo, minfo Get elastic consumer group member info + step-down, stepdown, sd Initiate a step down for a member + consume, join Join an elastic partitioned consumer group + prompt Interactive prompt mode +``` + +## Building from Source +The project contains both a Maven pom.xml file and a Gradle project, +and both are configured to build an executable Java jar named `cg.jar` + +### Maven +``` +mvn clean package +``` + +will build the `cg.jar` in the `target` folder + +### Gradle +``` +gradle clean package +``` +will build the `cg.jar` in the `build` folder + +## Running + + +``` +java -jar target/cg.jar ... +java -jar build/cg.jar ... +``` + +--- +Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index 91d173f..49e6271 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -1,17 +1,10 @@ plugins { id("java") - id("java-library") - id("maven-publish") - id("jacoco") - id("biz.aQute.bnd.builder") version "7.1.0" - id("org.gradle.test-retry") version "1.6.4" - id("io.github.gradle-nexus.publish-plugin") version "2.0.0" - id("signing") + id 'com.github.johnrengelman.shadow' version '8.1.1' // Apply the shadow plugin } -group = 'io.synadia' version = "0.1.0" -def originalUber = 'pcg-cli-' + version + '-uber.jar' +def originalShadow = 'pcg-cli-' + version + '-all.jar' java { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -27,7 +20,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.25.1' implementation 'org.jspecify:jspecify:1.0.0' - implementation 'io.synadia:pcgroups:0.1.0-SNAPSHOT' + implementation 'io.synadia:pcgroups:0.1.0' implementation 'info.picocli:picocli:4.7.5' testImplementation 'io.nats:jnats-server-runner:3.1.0' @@ -35,36 +28,39 @@ dependencies { testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } -tasks.register('copyToLib', Copy) { - into "build/libs" - from configurations.runtimeClasspath +shadowJar { + manifest { + attributes 'Main-Class': 'io.synadia.pcg.cli.CgCommand' + } } -tasks.register('packageUberJar', Jar) { - archiveClassifier = 'uber' +tasks.register('makecg', Copy) { + dependsOn shadowJar - from sourceSets.main.output + // we want the file to be called cg.jar + from ('build/libs') + include originalShadow + destinationDir file('build/') + rename originalShadow, "cg.jar" +} - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } - } +tasks.register('createZip', Zip) { + dependsOn makecg - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - dependsOn copyToLib + archiveFileName = "cg.zip" + destinationDirectory = file('build/') + from (files('build/cg.jar')) } -tasks.register('uberJar', Copy) { - dependsOn packageUberJar +tasks.register('createTar', Tar) { + dependsOn makecg - // we want the file to be called cg.jar - from ('build/libs') - include originalUber - destinationDir file('build/libs/') - rename originalUber, "cg.jar" + archiveFileName = "cg.tar" + destinationDirectory = file('build/') + from (files('build/cg.jar')) +} - // this task actually does a copy, so this part removes the original - doLast { - delete('build/libs/' + originalUber) - } +tasks.register('package') { + dependsOn createZip + dependsOn createTar } diff --git a/pcgroups/README.md b/pcgroups/README.md index 331021d..e59ff56 100644 --- a/pcgroups/README.md +++ b/pcgroups/README.md @@ -2,9 +2,17 @@ # Partitioned Consumer Groups +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:pcgroups-197556?labelColor=grey&style=flat) +![0.1.0](https://img.shields.io/badge/Current_Release-0.1.0-27AAE0) +![0.1.1](https://img.shields.io/badge/Current_Snapshot-0.1.1--SNAPSHOT-27AAE0) +[![Dependencies Help](https://img.shields.io/badge/Dependencies%20Help-27AAE0)](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) +[![javadoc](https://javadoc.io/badge2/io.synadia/pcgroups/javadoc.svg)](https://javadoc.io/doc/io.synadia/pcgroups) +[![Maven Central](https://img.shields.io/maven-central/v/io.synadia/pcgroups)](https://img.shields.io/maven-central/v/io.synadia/pcgroups) + Initial implementation of a client-side partitioned consumer group feature for NATS streams leveraging some of the new features introduced in `nats-server` version 2.11. Note that post 2.11 versions of `nats-server` may include new features related to the consumer group use case that could render this client-side library unneeded (or make much smaller) + # Overview This library enables the parallelization through partitioning of the consumption of messages from a stream while ensuring a strict order of not just delivery but also successful consumption of the messages using all or parts of the message's subject as a partitioning key. @@ -54,6 +62,9 @@ Included is a small command line interface tool, named `cg` and located in the ` This `cg` CLI tool can be used by passing it commands and arguments directly, or with an interactive prompt using the `prompt` command (e.g. `cg static prompt`). +For more details on the CLI visit the [Partitioned Consumer Groups CLI Project](https://github.com/synadia-io/orbit.java/tree/main/pcgroups-cli) + + ## Demo walkthrough ### Static diff --git a/pcgroups/build.gradle b/pcgroups/build.gradle index 75f3332..225e274 100644 --- a/pcgroups/build.gradle +++ b/pcgroups/build.gradle @@ -11,7 +11,7 @@ plugins { id("signing") } -def jarVersion = "0.1.0" +def jarVersion = "0.1.1" group = 'io.synadia' def isRelease = System.getenv("BUILD_EVENT") == "release" From 001340a71763d3ac7077dec5331d98b4e599e184 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 17:16:27 -0500 Subject: [PATCH 114/135] PCG-CLI build and readme --- .github/workflows/pcgcli-main.yml | 20 ++++++++++++++++---- .github/workflows/pcgcli-pr.yml | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pcgcli-main.yml b/.github/workflows/pcgcli-main.yml index 3c2c42d..2e1e681 100644 --- a/.github/workflows/pcgcli-main.yml +++ b/.github/workflows/pcgcli-main.yml @@ -26,17 +26,29 @@ jobs: chmod +x gradlew && ./gradlew clean test uberJar - name: Build with Maven run: mvn clean package - - name: Validate Gradle Executable Jar was created + - name: Validate artifacts were created run: | - FILE_PATH="build/libs/cg.jar" + FILE_PATH="build/cg.jar" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi + FILE_PATH="build/cg.zip" + if [ -f "$FILE_PATH" ]; then + echo "Validation successful: $FILE_PATH was created." + else + echo "Validation failed: $FILE_PATH was not found." + exit 1 # Fails the workflow step + fi + FILE_PATH="build/libs/cg.tar" if [ -f "$FILE_PATH" ]; then echo "Validation successful: $FILE_PATH was created." else echo "Validation failed: $FILE_PATH was not found." exit 1 # Fails the workflow step fi - - name: Validate Maven Executable Jar was created - run: | FILE_PATH="target/cg.jar" if [ -f "$FILE_PATH" ]; then echo "Validation successful: $FILE_PATH was created." diff --git a/.github/workflows/pcgcli-pr.yml b/.github/workflows/pcgcli-pr.yml index 250888d..43b15c3 100644 --- a/.github/workflows/pcgcli-pr.yml +++ b/.github/workflows/pcgcli-pr.yml @@ -27,7 +27,7 @@ jobs: run: mvn clean package - name: Validate artifacts were created run: | - FILE_PATH="build/libs/cg.jar" + FILE_PATH="build/cg.jar" if [ -f "$FILE_PATH" ]; then echo "Validation successful: $FILE_PATH was created." else From f4992da19b8dae046217c8f5f4cf4492fcc520a6 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 17:29:02 -0500 Subject: [PATCH 115/135] PCG-CLI build and readme --- .github/workflows/pcgcli-main.yml | 58 ------------------- .../workflows/{pcgcli-pr.yml => pcgcli.yml} | 10 ++-- pcgroups-cli/build.gradle | 8 +-- 3 files changed, 10 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/pcgcli-main.yml rename .github/workflows/{pcgcli-pr.yml => pcgcli.yml} (90%) diff --git a/.github/workflows/pcgcli-main.yml b/.github/workflows/pcgcli-main.yml deleted file mode 100644 index 2e1e681..0000000 --- a/.github/workflows/pcgcli-main.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Partitioned Consumer Groups CLI Main Snapshot - -on: - push: - branches: - - main - paths: - - 'pcgroups-cli/**' - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./pcgroups-cli - steps: - - name: Set up JDK - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - - name: Check out code - uses: actions/checkout@v4 - - name: Build with Gradle - run: | - chmod +x gradlew && ./gradlew clean test uberJar - - name: Build with Maven - run: mvn clean package - - name: Validate artifacts were created - run: | - FILE_PATH="build/cg.jar" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi - FILE_PATH="build/cg.zip" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi - FILE_PATH="build/libs/cg.tar" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi - FILE_PATH="target/cg.jar" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi diff --git a/.github/workflows/pcgcli-pr.yml b/.github/workflows/pcgcli.yml similarity index 90% rename from .github/workflows/pcgcli-pr.yml rename to .github/workflows/pcgcli.yml index 43b15c3..569c145 100644 --- a/.github/workflows/pcgcli-pr.yml +++ b/.github/workflows/pcgcli.yml @@ -1,6 +1,9 @@ -name: Partitioned Consumer Groups CLI Pull Request +name: Partitioned Consumer Groups CLI Build on: + push: + branches: + - main pull_request: types: [opened, synchronize, reopened] paths: @@ -21,8 +24,7 @@ jobs: - name: Check out code uses: actions/checkout@v4 - name: Build with Gradle - run: | - chmod +x gradlew && ./gradlew clean package + run: chmod +x gradlew && ./gradlew clean dist - name: Build with Maven run: mvn clean package - name: Validate artifacts were created @@ -41,7 +43,7 @@ jobs: echo "Validation failed: $FILE_PATH was not found." exit 1 # Fails the workflow step fi - FILE_PATH="build/libs/cg.tar" + FILE_PATH="build/cg.tar" if [ -f "$FILE_PATH" ]; then echo "Validation successful: $FILE_PATH was created." else diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index 49e6271..094794d 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -34,7 +34,7 @@ shadowJar { } } -tasks.register('makecg', Copy) { +tasks.register('package', Copy) { dependsOn shadowJar // we want the file to be called cg.jar @@ -45,7 +45,7 @@ tasks.register('makecg', Copy) { } tasks.register('createZip', Zip) { - dependsOn makecg + dependsOn 'package' archiveFileName = "cg.zip" destinationDirectory = file('build/') @@ -53,14 +53,14 @@ tasks.register('createZip', Zip) { } tasks.register('createTar', Tar) { - dependsOn makecg + dependsOn 'package' archiveFileName = "cg.tar" destinationDirectory = file('build/') from (files('build/cg.jar')) } -tasks.register('package') { +tasks.register('dist') { dependsOn createZip dependsOn createTar } From 83fd773018d81dcafa44e7961ddc829f94d8df4f Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 24 Feb 2026 17:48:16 -0500 Subject: [PATCH 116/135] PCG/CLI readme updates for cli binaries --- pcgroups-cli/README.md | 10 ++++++++++ pcgroups-cli/build.gradle | 4 ++-- pcgroups/README.md | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pcgroups-cli/README.md b/pcgroups-cli/README.md index 52014c5..30a9a7f 100644 --- a/pcgroups-cli/README.md +++ b/pcgroups-cli/README.md @@ -4,6 +4,16 @@ The Partitioned Consumer Groups CLI is a command line tool. In the usage, +[![0.1.0](https://img.shields.io/badge/Current_Release-0.1.0-27AAE0)](https://github.com/synadia-io/orbit.java/releases/tag/pcgcli%2F0.1.0) + +#### Downloads + +| tar | zip | +|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| +| [cg.tar](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.tar) | [cg.zip](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.zip) | + +## Usage + `cg` stands for `java -jar /cg.jar` ``` diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index 094794d..54d7b0f 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -49,7 +49,7 @@ tasks.register('createZip', Zip) { archiveFileName = "cg.zip" destinationDirectory = file('build/') - from (files('build/cg.jar')) + from (files('README.md', 'build/cg.jar')) } tasks.register('createTar', Tar) { @@ -57,7 +57,7 @@ tasks.register('createTar', Tar) { archiveFileName = "cg.tar" destinationDirectory = file('build/') - from (files('build/cg.jar')) + from (files('README.md', 'build/cg.jar')) } tasks.register('dist') { diff --git a/pcgroups/README.md b/pcgroups/README.md index e59ff56..b3642f9 100644 --- a/pcgroups/README.md +++ b/pcgroups/README.md @@ -64,6 +64,11 @@ This `cg` CLI tool can be used by passing it commands and arguments directly, or For more details on the CLI visit the [Partitioned Consumer Groups CLI Project](https://github.com/synadia-io/orbit.java/tree/main/pcgroups-cli) +### Binaries +You can download the latest `cg.jar` archived in a tar file +[cg.tar](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.tar) +or zip file +[cg.zip](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.zip) ## Demo walkthrough From 279ddad9d32f39bfba547ef9590ecd00f1e3129c Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 4 Mar 2026 15:58:52 -0500 Subject: [PATCH 117/135] Readme update and housekeeping --- README.md | 25 +- build-tester/CODE-OF-CONDUCT.md | 132 --------- build-tester/LICENSE | 201 -------------- build-tester/NOTICE | 5 - build-tester/README.md | 3 - build-tester/env.bat | 2 - build-tester/gradle/libs.versions.toml | 12 - .../gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - build-tester/gradlew | 251 ------------------ build-tester/gradlew.bat | 94 ------- .../bt/examples/BuildTesterExample.java | 8 - .../src/examples/resources/example.properties | 1 - .../main/java/io/synadia/bt/BuildTester.java | 10 - .../src/main/javadoc/images/favicon.ico | Bin 15406 -> 0 bytes .../src/main/javadoc/images/nats-logo.png | Bin 6533 -> 0 bytes .../src/main/javadoc/images/synadia-logo.png | Bin 19014 -> 0 bytes build-tester/src/main/javadoc/overview.html | 16 -- .../java/io/synadia/bt/BuildTesterTest.java | 17 -- .../src/test/resources/test.properties | 1 - pcgroups-cli/README.md | 9 +- 21 files changed, 19 insertions(+), 775 deletions(-) delete mode 100644 build-tester/CODE-OF-CONDUCT.md delete mode 100644 build-tester/LICENSE delete mode 100644 build-tester/NOTICE delete mode 100644 build-tester/README.md delete mode 100644 build-tester/env.bat delete mode 100644 build-tester/gradle/libs.versions.toml delete mode 100644 build-tester/gradle/wrapper/gradle-wrapper.jar delete mode 100644 build-tester/gradle/wrapper/gradle-wrapper.properties delete mode 100644 build-tester/gradlew delete mode 100644 build-tester/gradlew.bat delete mode 100644 build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java delete mode 100644 build-tester/src/examples/resources/example.properties delete mode 100644 build-tester/src/main/java/io/synadia/bt/BuildTester.java delete mode 100644 build-tester/src/main/javadoc/images/favicon.ico delete mode 100644 build-tester/src/main/javadoc/images/nats-logo.png delete mode 100644 build-tester/src/main/javadoc/images/synadia-logo.png delete mode 100644 build-tester/src/main/javadoc/overview.html delete mode 100644 build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java delete mode 100644 build-tester/src/test/resources/test.properties diff --git a/README.md b/README.md index c7f48fa..538c89d 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,19 @@ boost productivity and provide a higher abstraction layer for the [JNATS](https: client. Note that these libraries will evolve rapidly and API guarantees are general not made until the specific project has a v1.0.0 version. # Utilities -| Module | Description | Docs | Release Version | Snapshot | -|------------------------------|---------------------------------------------------------|----------------------------------------------|-----------------|----------------| -| Retrier | Extension for retrying anything | [README.md](retrier/README.md) | 0.2.1 | 0.2.2-SNAPSHOT | -| Jetstream Publish Extensions | General extensions for Jetstream Publishing | [README.md](js-publish-extensions/README.md) | 0.4.4 | 0.4.5-SNAPSHOT | -| Request Many | Get many responses for a single core request. | [README.md](request-many/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | -| Encoded KeyValue | Allow custom encoding of keys and values. | [README.md](encoded-kv/README.md) | 0.1.1 | 0.1.2-SNAPSHOT | -| Direct Batch | Leverages direct message capabilities in NATS Server | [README.md](direct-batch/README.md) | 0.0.4 | 0.0.5-SNAPSHOT | -| Batch Publish | Publish an atomic batch | [README.md](batch-publish/README.md) | 0.2.2 | 0.2.3-SNAPSHOT | -| Distributed Counters | Leverage distributed counters functionality | [README.md](counters/README.md) | 0.2.2 | 0.2.3-SNAPSHOT | -| Scheduled Message | Leverage ability to schedule a message | [README.md](schedule-message/README.md) | 0.0.3 | 0.0.4-SNAPSHOT | -| Chaos Runner | Run some NATS servers and cause chaos | [README.md](chaos-runner/README.md) | 0.0.8 | 0.0.9-SNAPSHOT | -| Partitioned Consumer Groups | Partitioned Consumer Group funcitionality for JetStream | [README.md](pcgroups/README.md) | 0.1.1 | 0.1.1-SNAPSHOT | +| Project | Description | Release Version | Snapshot | +|-------------------------------------------------------|---------------------------------------------------------|-----------------|----------------| +| [Retrier](retrier) | Extension for retrying anything | 0.2.1 | 0.2.2-SNAPSHOT | +| [Jetstream Publish Extensions](js-publish-extensions) | General extensions for Jetstream Publishing | 0.4.4 | 0.4.5-SNAPSHOT | +| [Request Many](request-many) | Get many responses for a single core request. | 0.1.1 | 0.1.2-SNAPSHOT | +| [Encoded KeyValue](encoded-kv) | Allow custom encoding of keys and values. | 0.1.1 | 0.1.2-SNAPSHOT | +| [Direct Batch](direct-batch) | Leverages direct message capabilities in NATS Server | 0.0.4 | 0.0.5-SNAPSHOT | +| [Batch Publish](batch-publish) | Publish an atomic batch | 0.2.2 | 0.2.3-SNAPSHOT | +| [Distributed Counters](counters) | Leverage distributed counters functionality | 0.2.2 | 0.2.3-SNAPSHOT | +| [Scheduled Message](schedule-message) | Leverage ability to schedule a message | 0.0.3 | 0.0.4-SNAPSHOT | +| [Chaos Runner](chaos-runner) | Run some NATS servers and cause chaos | 0.0.8 | 0.0.9-SNAPSHOT | +| [Partitioned Consumer Groups](pcgroups) | Partitioned Consumer Group funcitionality for JetStream | 0.1.1 | 0.1.1-SNAPSHOT | +| [Partitioned Consumer Groups CLI](pcgroups-cli) | Partitioned Consumer Group CLI | 0.1.0 | N/A | ## Retrier diff --git a/build-tester/CODE-OF-CONDUCT.md b/build-tester/CODE-OF-CONDUCT.md deleted file mode 100644 index 7eb3884..0000000 --- a/build-tester/CODE-OF-CONDUCT.md +++ /dev/null @@ -1,132 +0,0 @@ -## Community Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[INSERT CONTACT METHOD]. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations diff --git a/build-tester/LICENSE b/build-tester/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/build-tester/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed 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. diff --git a/build-tester/NOTICE b/build-tester/NOTICE deleted file mode 100644 index 0e589c7..0000000 --- a/build-tester/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -Flink Connector For NATS -Copyright (c) 2023-2025 Synadia Communications Inc. All Rights Reserved. - -This product includes software developed at -Synadia Communications Inc. \ No newline at end of file diff --git a/build-tester/README.md b/build-tester/README.md deleted file mode 100644 index ad06d82..0000000 --- a/build-tester/README.md +++ /dev/null @@ -1,3 +0,0 @@ -![Synadia](src/main/javadoc/images/synadia-logo.png) - -# Build Tester diff --git a/build-tester/env.bat b/build-tester/env.bat deleted file mode 100644 index 3772712..0000000 --- a/build-tester/env.bat +++ /dev/null @@ -1,2 +0,0 @@ -set JAVA_HOME=C:\Program Files\Java\jdk-21 -set PATH=C:\Program Files\Java\jdk-21\bin;%PATH% diff --git a/build-tester/gradle/libs.versions.toml b/build-tester/gradle/libs.versions.toml deleted file mode 100644 index 2cfe86a..0000000 --- a/build-tester/gradle/libs.versions.toml +++ /dev/null @@ -1,12 +0,0 @@ -# This file was generated by the Gradle 'init' task. -# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format - -[versions] -commons-math3 = "3.6.1" -guava = "33.4.5-jre" -junit-jupiter = "5.12.1" - -[libraries] -commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } -guava = { module = "com.google.guava:guava", version.ref = "guava" } -junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/build-tester/gradle/wrapper/gradle-wrapper.jar b/build-tester/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 1b33c55baabb587c669f562ae36f953de2481846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43764 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH="\\\"\\\"" - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/build-tester/gradlew.bat b/build-tester/gradlew.bat deleted file mode 100644 index db3a6ac..0000000 --- a/build-tester/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java b/build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java deleted file mode 100644 index 2047a8f..0000000 --- a/build-tester/src/examples/java/io/synadia/bt/examples/BuildTesterExample.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.synadia.bt.examples; - -public class BuildTesterExample { - - public static void main(String[] args) { - System.out.println("BuildTesterExample"); - } -} \ No newline at end of file diff --git a/build-tester/src/examples/resources/example.properties b/build-tester/src/examples/resources/example.properties deleted file mode 100644 index e869deb..0000000 --- a/build-tester/src/examples/resources/example.properties +++ /dev/null @@ -1 +0,0 @@ -io.nats.client.url=nats://localhost:4222 diff --git a/build-tester/src/main/java/io/synadia/bt/BuildTester.java b/build-tester/src/main/java/io/synadia/bt/BuildTester.java deleted file mode 100644 index bc0ca82..0000000 --- a/build-tester/src/main/java/io/synadia/bt/BuildTester.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.synadia.bt; - -public class BuildTester { - - public final int x; - - public BuildTester(int x) { - this.x = x; - } -} \ No newline at end of file diff --git a/build-tester/src/main/javadoc/images/favicon.ico b/build-tester/src/main/javadoc/images/favicon.ico deleted file mode 100644 index 430e80fcadbda98f8b8a017b86ed7525744c07bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 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 - - - - -Build Tester. -

    Synadia Logo

    - -Build Tester -

    This package is strictly to test builds.

    - - - - diff --git a/build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java b/build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java deleted file mode 100644 index 09b22b6..0000000 --- a/build-tester/src/test/java/io/synadia/bt/BuildTesterTest.java +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2023-2025 Synadia Communications Inc. All Rights Reserved. -// See LICENSE and NOTICE file for details. - -package io.synadia.bt; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class BuildTesterTest { - - @Test - public void testBuildTester() throws Exception { - BuildTester bt = new BuildTester(42); - assertEquals(42, bt.x); - } -} diff --git a/build-tester/src/test/resources/test.properties b/build-tester/src/test/resources/test.properties deleted file mode 100644 index e869deb..0000000 --- a/build-tester/src/test/resources/test.properties +++ /dev/null @@ -1 +0,0 @@ -io.nats.client.url=nats://localhost:4222 diff --git a/pcgroups-cli/README.md b/pcgroups-cli/README.md index 30a9a7f..2fb749c 100644 --- a/pcgroups-cli/README.md +++ b/pcgroups-cli/README.md @@ -8,9 +8,12 @@ The Partitioned Consumer Groups CLI is a command line tool. In the usage, #### Downloads -| tar | zip | -|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| -| [cg.tar](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.tar) | [cg.zip](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.zip) | +Archives containing executable jar file (from the [Release Page](https://github.com/synadia-io/orbit.java/releases/tag/pcgcli%2F0.1.0)) + +| Asset | Size | SHA | +|------------------------------------------------------------------------------------------------|---------|---------------------------------------------------------------------------| +| **[cg.tar](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.tar)** | 6.56 MB | `sha256:7d02bfb4246872929613a029ade1ac1f2edc25abdd60140a9fd8a36452977f1e` | +| **[cg.zip](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.zip)** | 5.65 MB | `sha256:dff6e5b85377cac79635e6d77ecd65de35fe4031687ecefd0c2302b6fd520be2` | ## Usage From a0cf34f350936bd8dfac395f1b448719e7d29e42 Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 5 Mar 2026 13:42:55 -0500 Subject: [PATCH 118/135] Scheduling sources --- build-tester/build.gradle | 206 ------------------ build-tester/settings.gradle | 8 - schedule-message/build.gradle | 3 +- .../io/synadia/examples/ScheduleBasics.java | 16 +- .../synadia/examples/ScheduleFromSource.java | 108 +++++++++ ...leExampleUtils.java => ScheduleUtils.java} | 2 +- .../synadia/sm/ScheduledMessageBuilder.java | 63 +++++- 7 files changed, 180 insertions(+), 226 deletions(-) delete mode 100644 build-tester/build.gradle delete mode 100644 build-tester/settings.gradle create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java rename schedule-message/src/examples/java/io/synadia/examples/{ScheduleExampleUtils.java => ScheduleUtils.java} (97%) diff --git a/build-tester/build.gradle b/build-tester/build.gradle deleted file mode 100644 index 323a6cc..0000000 --- a/build-tester/build.gradle +++ /dev/null @@ -1,206 +0,0 @@ -import aQute.bnd.gradle.Bundle - -plugins { - id("java") - id("java-library") - id("maven-publish") - id("jacoco") - id("biz.aQute.bnd.builder") version "7.1.0" - id("org.gradle.test-retry") version "1.6.4" - id("io.github.gradle-nexus.publish-plugin") version "2.0.0" - id("signing") -} - -def jarVersion = "0.0.7" -group = 'io.synadia' - -def isRelease = System.getenv("BUILD_EVENT") == "release" - -def tc = System.getenv("TARGET_COMPATIBILITY"); -def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) -def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") -def jarAndArtifactName = "build-tester" + jarEnd - -version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = targetCompat -} - -repositories { - mavenCentral() - maven { url="https://repo1.maven.org/maven2/" } - maven { url="https://central.sonatype.com/repository/maven-snapshots" } -} - -dependencies { - implementation 'io.nats:jnats:2.25.1' - implementation 'org.jspecify:jspecify:1.0.0' - implementation 'io.synadia:counters:0.2.1-SNAPSHOT' - - testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' - testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' -} - -sourceSets { - main { - java { - srcDirs = ['src/main/java','src/examples/java'] - } - resources { - srcDirs = ['src/examples/resources'] - } - } - test { - java { - srcDirs = ['src/test/java'] - } - } -} - -tasks.register('bundle', Bundle) { - from sourceSets.main.output - exclude("io/synadia/bt/examples/**") -} - -jar { - bundle { - bnd("Bundle-Name": "io.synadia.build.tester", - "Bundle-Vendor": "synadia.io", - "Bundle-Description": "Build Tester", - "Bundle-DocURL": "https://synadia.io" - ) - } -} - -test { - // Use junit platform for unit tests - useJUnitPlatform() - testLogging { - exceptionFormat = 'full' - events "started", "passed", "skipped", "failed" - showStandardStreams = true - } - retry { - failOnPassedAfterRetry = false - maxFailures = 3 - maxRetries = 3 - } - systemProperty 'junit.jupiter.execution.timeout.default', '3m' -} - -javadoc { - options.overview = 'src/main/javadoc/overview.html' // relative to source root - source = sourceSets.main.allJava - title = "Synadia Communications Inc. Build Tester" - excludes ['**/examples/**'] - classpath = sourceSets.main.runtimeClasspath -} - -tasks.register('examplesJar', Jar) { - archiveClassifier.set('examples') - manifest { - attributes('Implementation-Title': 'Build Tester Examples', - 'Implementation-Version': jarVersion, - 'Implementation-Vendor': 'synadia.io') - } - from(sourceSets.main.output) { - include "io/synadia/bt/examples/**" - } -} - -tasks.register('javadocJar', Jar) { - archiveClassifier.set('javadoc') - from javadoc -} - -tasks.register('sourcesJar', Jar) { - archiveClassifier.set('sources') - from sourceSets.main.allSource -} - -tasks.register('testsJar', Jar) { - archiveClassifier.set('tests') - from sourceSets.test.allSource -} - -artifacts { - archives javadocJar, sourcesJar, examplesJar, testsJar -} - -jacoco { - toolVersion = "0.8.12" -} - -jacocoTestReport { - reports { - xml.required = true // coveralls plugin depends on xml format report - html.required = true - } - afterEvaluate { // only report on main library not examples - classDirectories.setFrom(files(classDirectories.files.collect { - fileTree(dir: it, - exclude: ['**/examples**','**/Debug**']) - })) - } -} - -nexusPublishing { - repositories { - sonatype { - nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) - snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) - username = System.getenv('OSSRH_USERNAME') - password = System.getenv('OSSRH_PASSWORD') - } - } -} - -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact examplesJar - artifact javadocJar - artifact testsJar - pom { - name = jarAndArtifactName - packaging = "jar" - groupId = group - artifactId = jarAndArtifactName - description = "Synadia Communications Inc. Build Tester" - url = "https://github.com/synadia-io/orbit.java/tree/main/build-tester" - licenses { - license { - name = 'The Apache License, Version 2.0' - url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id = "synadia" - name = "Synadia" - email = "info@synadia.com" - url = "https://synadia.io" - } - } - scm { - url = "https://github.com/synadia-io/orbit.java" - } - } - } - } -} - -if (isRelease) { - signing { - def signingKeyId = System.getenv('SIGNING_KEY_ID') - def signingKey = System.getenv('SIGNING_KEY') - def signingPassword = System.getenv('SIGNING_PASSWORD') - useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) - sign configurations.archives - sign publishing.publications.mavenJava - } -} diff --git a/build-tester/settings.gradle b/build-tester/settings.gradle deleted file mode 100644 index 34bf496..0000000 --- a/build-tester/settings.gradle +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenCentral() - maven { url "https://oss.sonatype.org/content/repositories/releases/" } - maven { url "https://plugins.gradle.org/m2/" } - } -} -rootProject.name = 'build-tester' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index f42c512..1f1fcf0 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -29,13 +29,14 @@ java { } repositories { + mavenLocal() mavenCentral() maven { url="https://repo1.maven.org/maven2/" } maven { url="https://central.sonatype.com/repository/maven-snapshots" } } dependencies { - implementation 'io.nats:jnats:2.25.1' + implementation 'io.nats:jnats:2.25.3-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' implementation 'io.synadia:counters:0.2.2' diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java index fb26069..631a00b 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java @@ -13,10 +13,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.synadia.examples.ScheduleExampleUtils.report; +import static io.synadia.examples.ScheduleUtils.report; public class ScheduleBasics { - public static final String STREAM = "scheduler"; + public static final String STREAM = "schedules-enabled"; public static final String SCHEDULE_PREFIX = "schedule."; public static final String TARGET_PREFIX = "target."; @@ -33,7 +33,7 @@ public static void main(String[] args) { .errorListener(new ErrorListener() {}) .build(); - try (Connection connection = Nats.connectReconnectOnConnect(options)) { + try (Connection connection = Nats.connect(options)) { JetStreamManagement jsm = connection.jetStreamManagement();; JetStream js = connection.jetStream(); @@ -49,13 +49,13 @@ public static void main(String[] args) { // subscribe to the subject that receives the schedule message js.subscribe(SCHEDULES, d, m -> { - report("SCHEDULED", m); + report("SCHEDULED (received)", m); m.ack(); }, false); // subscribe to the target subject js.subscribe(TARGETS, d, m -> { - report("RECEIVED", m); + report("TARGETED (received)", m); m.ack(); latch.countDown(); }, false); @@ -66,7 +66,7 @@ public static void main(String[] args) { .scheduleImmediate() .data("Schedule-Now") .build(); - report("PUBLISH", m); + report("SCHEDULE-NOW (sending)", m); js.publish(m); m = new ScheduledMessageBuilder() @@ -75,7 +75,7 @@ public static void main(String[] args) { .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(5)) .data("Scheduled-At") .build(); - report("PUBLISH", m); + report("SCHEDULE-AT (sending)", m); js.publish(m); m = new ScheduledMessageBuilder() @@ -84,7 +84,7 @@ public static void main(String[] args) { .scheduleEvery(1, TimeUnit.SECONDS) .data("Every Second") .build(); - report("PUBLISH", m); + report("SCHEDULE-EVERY (sending)", m); js.publish(m); latch.await(); diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java new file mode 100644 index 0000000..0627381 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java @@ -0,0 +1,108 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamInfo; +import io.nats.client.impl.Headers; +import io.nats.client.impl.NatsMessage; +import io.synadia.sm.ScheduledMessageBuilder; +import io.synadia.sm.ScheduledStreamUtil; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; + +import static io.synadia.examples.ScheduleUtils.report; + +public class ScheduleFromSource { + public static final String STREAM = "schedules-enabled"; + + private static final String SCHEDULES = "schedules"; + private static final String TARGET = "target"; + private static final String SOURCE = "source"; + + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGET, SOURCE}; + + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + // Use the utility to properly create a schedulable stream + StreamInfo si = ScheduledStreamUtil.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + report("Created stream", si.getConfiguration()); + + CountDownLatch latch1 = new CountDownLatch(1); + CountDownLatch latch2 = new CountDownLatch(2); + Dispatcher d = connection.createDispatcher(); + + // subscribe to the subject that receives the schedule message + js.subscribe(SCHEDULES, d, m -> { + report("SCHEDULED (received)", m); + m.ack(); + }, false); + + // subscribe to the target subject + js.subscribe(SOURCE, d, m -> { + report("SOURCED (received)", m); + m.ack(); + }, false); + + // subscribe to the target subject + js.subscribe(TARGET, d, m -> { + report("TARGETED (received)", m); + m.ack(); + latch1.countDown(); + latch2.countDown(); + }, false); + + // Publish Data to the Source subject + String sourceData = "data1"; + Headers sourceHeaders = new Headers(); + sourceHeaders.put("foo1", "bar1"); + Message sourceMessage = new NatsMessage(SOURCE, null, sourceHeaders, sourceData.getBytes()); + report("SOURCE 1 (sending)", sourceMessage); + js.publish(sourceMessage); + connection.flush(Duration.ofSeconds(1)); + + Message scheduleMessage = new ScheduledMessageBuilder() + .scheduleSubject(SCHEDULES) + .targetSubject(TARGET) + .scheduleImmediate() + .sources(SOURCE) + .build(); + report("SCHEDULE 1 (sending)", scheduleMessage); + js.publish(scheduleMessage); + + latch1.await(); + + sourceData = "data2"; + sourceHeaders = new Headers(); + sourceHeaders.put("foo2", "bar2"); + sourceMessage = new NatsMessage(SOURCE, null, sourceHeaders, sourceData.getBytes()); + report("SOURCE 2 (sending)", sourceMessage); + js.publish(sourceMessage); + connection.flush(Duration.ofSeconds(1)); + + report("SCHEDULE 2 (sending)", scheduleMessage); + js.publish(scheduleMessage); + + latch2.await(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java similarity index 97% rename from schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java rename to schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java index 9e76501..fac2489 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java @@ -6,7 +6,7 @@ import io.nats.client.Message; import io.nats.client.impl.Headers; -public class ScheduleExampleUtils { +public class ScheduleUtils { public static void report(Object... objects) { StringBuilder sb = new StringBuilder(); diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index 6401be2..4725560 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -16,6 +16,9 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -25,11 +28,13 @@ public class ScheduledMessageBuilder { public static final long NANOS_PER_SECOND = 1_000_000_000L; private String scheduleString; + private String timezone; private String scheduleSubject; private String targetSubject; private Headers headers; private byte[] data; private MessageTtl messageTtl; + private final List sources = new ArrayList<>(); public ScheduledMessageBuilder() {} @@ -88,7 +93,7 @@ public ScheduledMessageBuilder data(String data, final Charset charset) { } /** - * Set the headers + * Set message headers * @param headers the headers * @return the builder */ @@ -108,7 +113,27 @@ public ScheduledMessageBuilder copy(Message message) { } /** - * Schedule for at a specific time + * Schedule for an amount of time from now. + * This is not absolute since it takes time to build and send the message. + * @param fromNow how long from now to schedule + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleIn(Duration fromNow) { + return scheduleAt(ZonedDateTime.now().plus(fromNow)); + } + + /** + * Schedule for an amount of time from now. + * This is not absolute since it takes time to build and send the message. + * @param fromNow how long from now to schedule + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleIn(long fromNow, TimeUnit timeUnit) { + return scheduleAt(ZonedDateTime.now().plusNanos(timeUnit.toNanos(fromNow))); + } + + /** + * Schedule for a specific time * @param zdt the time to schedule * @return a ScheduledMessageBuilder object */ @@ -191,11 +216,39 @@ public ScheduledMessageBuilder scheduleCron(String cron) { return this; } + /** + * Schedule based on standard cron + * @param cron A valid cron string + * @param timezone a valid IANA time zone + * @return a ScheduledMessageBuilder object + */ + public ScheduledMessageBuilder scheduleCron(String cron, String timezone) { + scheduleString = Validator.emptyAsNull(cron); + this.timezone = timezone; + return this; + } + public ScheduledMessageBuilder messageTtl(MessageTtl messageTtl) { this.messageTtl = messageTtl; return this; } + public ScheduledMessageBuilder sources(List sources) { + this.sources.clear(); + if (sources != null) { + this.sources.addAll(sources); + } + return this; + } + + public ScheduledMessageBuilder sources(String... sources) { + this.sources.clear(); + if (sources != null) { + Collections.addAll(this.sources, sources); + } + return this; + } + public Message build() { Validator.required(scheduleSubject, "Publish Subject is required."); Validator.required(targetSubject, "Target Subject is required."); @@ -215,6 +268,12 @@ public Message build() { if (messageTtl != null) { headers.put(NatsJetStreamConstants.NATS_SCHEDULE_TTL_HDR, messageTtl.getTtlString()); } + if (timezone != null) { + headers.put(NatsJetStreamConstants.NATS_SCHEDULE_TIME_ZONE_HDR, timezone); + } + if (sources.size() > 0) { + headers.put(NatsJetStreamConstants.NATS_SCHEDULE_SOURCE_HDR, sources); + } return NatsMessage.builder() .subject(scheduleSubject) From eb3ab8c4321a9c124bf183d0a4c63b659f938e1d Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 5 Mar 2026 13:51:44 -0500 Subject: [PATCH 119/135] Scheduling sources --- schedule-message/build.gradle | 40 ++++++++++++----------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 1f1fcf0..2a61b39 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -23,6 +23,10 @@ def jarAndArtifactName = "schedule-message" + jarEnd version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" // version is the variable the build actually uses. +System.out.println("Java: " + System.getProperty("java.version")) +System.out.println("Target Compatibility: " + targetCompat) +System.out.println(group + ":" + jarAndArtifactName + ":" + version) + java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = targetCompat @@ -32,7 +36,7 @@ repositories { mavenLocal() mavenCentral() maven { url="https://repo1.maven.org/maven2/" } - maven { url="https://central.sonatype.com/repository/maven-snapshots" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } } dependencies { @@ -40,6 +44,7 @@ dependencies { implementation 'org.jspecify:jspecify:1.0.0' implementation 'io.synadia:counters:0.2.2' + testImplementation 'io.nats:jnats-server-runner:3.1.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } @@ -49,9 +54,6 @@ sourceSets { java { srcDirs = ['src/main/java','src/examples/java'] } - resources { - srcDirs = ['src/examples/resources'] - } } test { java { @@ -62,7 +64,7 @@ sourceSets { tasks.register('bundle', Bundle) { from sourceSets.main.output - exclude("io/synadia/bt/examples/**") + exclude("io/synadia/examples/**") } jar { @@ -70,7 +72,8 @@ jar { bnd("Bundle-Name": "io.synadia.schedule.message", "Bundle-Vendor": "synadia.io", "Bundle-Description": "JetStream Scheduled Messages", - "Bundle-DocURL": "https://synadia.io" + "Bundle-DocURL": "https://github.com/synadia-io/orbit.java/tree/main/schedule-message", + "Target-Compatibility": "Java " + targetCompat ) } } @@ -78,17 +81,6 @@ jar { test { // Use junit platform for unit tests useJUnitPlatform() - testLogging { - exceptionFormat = 'full' - events "started", "passed", "skipped", "failed" - showStandardStreams = true - } - retry { - failOnPassedAfterRetry = false - maxFailures = 3 - maxRetries = 3 - } - systemProperty 'junit.jupiter.execution.timeout.default', '3m' } javadoc { @@ -107,7 +99,7 @@ tasks.register('examplesJar', Jar) { 'Implementation-Vendor': 'synadia.io') } from(sourceSets.main.output) { - include "io/synadia/bt/examples/**" + include "io/synadia/examples/**" } } @@ -121,13 +113,8 @@ tasks.register('sourcesJar', Jar) { from sourceSets.main.allSource } -tasks.register('testsJar', Jar) { - archiveClassifier.set('tests') - from sourceSets.test.allSource -} - artifacts { - archives javadocJar, sourcesJar, examplesJar, testsJar + archives javadocJar, sourcesJar, examplesJar } jacoco { @@ -165,10 +152,9 @@ publishing { artifact sourcesJar artifact examplesJar artifact javadocJar - artifact testsJar pom { name = jarAndArtifactName - packaging = "jar" + packaging = 'jar' groupId = group artifactId = jarAndArtifactName description = "Synadia Communications Inc. JetStream Scheduled Messages" @@ -188,7 +174,7 @@ publishing { } } scm { - url = "https://github.com/synadia-io/orbit.java" + url = 'https://github.com/synadia-io/orbit.java' } } } From 70c750c79937bb6f268d11aa86a1b0868dffe9df Mon Sep 17 00:00:00 2001 From: scottf Date: Thu, 5 Mar 2026 13:52:08 -0500 Subject: [PATCH 120/135] Claude Build Script --- .github/workflows/claude.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/claude.yml diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000..5290a5f --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,22 @@ +name: Claude Code Review + +permissions: + contents: read + pull-requests: write + issues: write + id-token: write + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +jobs: + claude: + uses: synadia-io/ai-workflows/.github/workflows/claude.yml@v2 + with: + gh_app_id: ${{ vars.CLAUDE_GH_APP_ID }} + secrets: + claude_oauth_token: ${{ secrets.CLAUDE_OAUTH_TOKEN }} + gh_app_private_key: ${{ secrets.CLAUDE_GH_APP_PRIVATE_KEY }} From 9ba10f5581caa6c12b5b6d87506527bb4122c79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Moyne?= Date: Thu, 12 Mar 2026 01:09:44 +0100 Subject: [PATCH 121/135] Elastic consumer groups: - enables not specifying any wildcard indexes, in which case the whole subject is used to compute the partition number. - Enables not specifying any partitioning filters, in which case ">" is used as the sole partition filter with the whole subject name being used to compute the partition number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port of https://github.com/synadia-io/orbit.go/pull/46 Signed-off-by: Jean-Noël Moyne --- pcgroups/README.md | 8 +-- .../io/synadia/pcg/ElasticConsumerGroup.java | 13 +++- .../pcg/ElasticConsumerGroupConfig.java | 60 +++++++++-------- .../synadia/pcg/ElasticConsumerGroupTest.java | 66 +++++++++++++++++-- .../java/io/synadia/pcg/IntegrationTest.java | 50 ++++++++++++++ 5 files changed, 155 insertions(+), 42 deletions(-) diff --git a/pcgroups/README.md b/pcgroups/README.md index b3642f9..f175860 100644 --- a/pcgroups/README.md +++ b/pcgroups/README.md @@ -27,7 +27,7 @@ NATS Partitioned consumer groups come in two flavors: *elastic* and *static*. ***Static*** partitioned consumer groups assume that the stream already has a partition number present as the first token of the message's subjects (something that can be done automatically when messages are stored into to the stream by setting a subject transform for the stream). You can only create and delete static consumer groups. Any change to the consumer group's config in the KV bucket will cause all the member instances for all members of the group to stop consuming. -***Elastic*** partitioned consumer groups on the other hand are implemented differently: the stream doesn't need to already contain a partition number subject token and you can administratively add and drop members from the consumer group's config whenever you want without having to delete and re-create the consumer (like you have to with static consumer groups). +***Elastic*** partitioned consumer groups on the other hand are implemented differently: the stream doesn't need to already contain a partition number subject token and you can administratively add and drop members from the consumer group's config whenever you want without having to delete and re-create the consumer (like you have to with static consumer groups). You have the option of specifying a subject filter for the consumer group and calculating the partition number from the subject name using a consistent hashing algorithm. Either through the use of `*` wildcards in the partitioning filter(s) and then specifying in the partitioning wildcards array the indexes of the `*` wildcards in the filter that you want to use for computing the partition number (you can specify between one index and all of the indexes), or by leaving that array of wildcard indexes empty (or not specifying a partitioning filter at all) in which case the partition number is calculated using the entirety of the message's subject. ***In both cases*** In both cases you must specify when creating the consumer group the maximum number of members for the group (which is actually the number of partitions used when partitioning the messages), plus a list of "members" (named instances of the consuming application). The library takes care of distributing the members over the list of partitions using either a 'balanced' distribution (the partitions are evenly distributed between the members) or 'mappings' (where you assign administratively the mappings of partitions to the members). The membership list or mappings must be specified once at consumer group creation time for static consumer groups, but can be changed at any time for elastic consumer groups. @@ -65,8 +65,8 @@ This `cg` CLI tool can be used by passing it commands and arguments directly, or For more details on the CLI visit the [Partitioned Consumer Groups CLI Project](https://github.com/synadia-io/orbit.java/tree/main/pcgroups-cli) ### Binaries -You can download the latest `cg.jar` archived in a tar file -[cg.tar](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.tar) +You can download the latest `cg.jar` archived in a tar file +[cg.tar](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.tar) or zip file [cg.zip](https://github.com/synadia-io/orbit.java/releases/download/pcgcli%2F0.1.0/cg.zip) @@ -117,4 +117,4 @@ Partitioned consumer groups require NATS server version 2.11 or above. --- Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. -See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. \ No newline at end of file diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java index a798323..29366be 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java @@ -111,6 +111,7 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName // Create the work queue stream with subject transform String workQueueStreamName = composeCGSName(streamName, consumerGroupName); + String effectiveFilter = (filter != null && !filter.isEmpty()) ? filter : ">"; String filterDest = getPartitioningTransformDest(config); StreamConfiguration.Builder scBuilder = StreamConfiguration.builder() @@ -129,12 +130,11 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName } // Add source with subject transform - External external = null; // Local stream scBuilder.addSource(Source.builder() .sourceName(streamName) .startSeq(0) .subjectTransforms(SubjectTransform.builder() - .source(filter) + .source(effectiveFilter) .destination(filterDest) .build()) .build()); @@ -534,14 +534,16 @@ private static ElasticConsumerGroupConfig getConfigFromKV(KeyValue kv, String st } private static String getPartitioningTransformDest(ElasticConsumerGroupConfig config) { + String effectiveFilter = (config.getFilter() != null && !config.getFilter().isEmpty()) ? config.getFilter() : ">"; int[] wildcards = config.getPartitioningWildcards(); + StringBuilder wildcardList = new StringBuilder(); for (int i = 0; i < wildcards.length; i++) { if (i > 0) wildcardList.append(","); wildcardList.append(wildcards[i]); } - String[] filterTokens = config.getFilter().split("\\."); + String[] filterTokens = effectiveFilter.split("\\."); int cwIndex = 1; for (int i = 0; i < filterTokens.length; i++) { if (filterTokens[i].equals("*")) { @@ -551,6 +553,11 @@ private static String getPartitioningTransformDest(ElasticConsumerGroupConfig co } String destFromFilter = String.join(".", filterTokens); + + if (wildcards.length == 0) { + return "{{Partition(" + config.getMaxMembers() + ")}}." + destFromFilter; + } + return "{{Partition(" + config.getMaxMembers() + "," + wildcardList + ")}}." + destFromFilter; } diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java index 8c66dc2..ccc098d 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java @@ -189,35 +189,31 @@ public void validate() throws ConsumerGroupException { } // Validate filter and partitioning wildcards - if (filter == null || filter.isEmpty()) { - throw new ConsumerGroupException("filter must not be empty"); - } - - String[] filterTokens = filter.split("\\."); - int numWildcards = 0; - for (String token : filterTokens) { - if ("*".equals(token)) { - numWildcards++; + if (filter != null && !filter.isEmpty()) { + String[] filterTokens = filter.split("\\."); + int numWildcards = 0; + for (String token : filterTokens) { + if ("*".equals(token)) { + numWildcards++; + } } - } - - if (numWildcards < 1) { - throw new ConsumerGroupException("filter must contain at least one * wildcard"); - } - - if (partitioningWildcards == null || partitioningWildcards.length < 1 || partitioningWildcards.length > numWildcards) { - throw new ConsumerGroupException("the number of partitioning wildcards must be between 1 and the total number of * wildcards in the filter"); - } - Set seenWildcards = new HashSet<>(); - for (int pwc : partitioningWildcards) { - if (seenWildcards.contains(pwc)) { - throw new ConsumerGroupException("partitioning wildcard indexes must be unique"); + if (partitioningWildcards != null && partitioningWildcards.length > numWildcards) { + throw new ConsumerGroupException("the number of partitioning wildcards must not be larger than the total number of * wildcards in the filter"); } - seenWildcards.add(pwc); - if (pwc < 1 || pwc > numWildcards) { - throw new ConsumerGroupException("partitioning wildcard indexes must be greater than 1 and less than or equal to the number of * wildcards in the filter"); + Set seenWildcards = new HashSet<>(); + if (partitioningWildcards != null) { + for (int pwc : partitioningWildcards) { + if (seenWildcards.contains(pwc)) { + throw new ConsumerGroupException("partitioning wildcard indexes must be unique"); + } + seenWildcards.add(pwc); + + if (pwc > numWildcards) { + throw new ConsumerGroupException("partitioning wildcard indexes must be less than or equal to the number of * wildcards in the filter"); + } + } } } @@ -266,15 +262,18 @@ public void validate() throws ConsumerGroupException { * Generates the subject transform destination for partitioning. */ public String getPartitioningTransformDest() { + String effectiveFilter = (filter != null && !filter.isEmpty()) ? filter : ">"; + int[] effectiveWildcards = (partitioningWildcards != null) ? partitioningWildcards : new int[0]; + StringBuilder wildcardList = new StringBuilder(); - for (int i = 0; i < partitioningWildcards.length; i++) { + for (int i = 0; i < effectiveWildcards.length; i++) { if (i > 0) { wildcardList.append(","); } - wildcardList.append(partitioningWildcards[i]); + wildcardList.append(effectiveWildcards[i]); } - String[] filterTokens = filter.split("\\."); + String[] filterTokens = effectiveFilter.split("\\."); int cwIndex = 1; for (int i = 0; i < filterTokens.length; i++) { if ("*".equals(filterTokens[i])) { @@ -284,6 +283,11 @@ public String getPartitioningTransformDest() { } String destFromFilter = String.join(".", filterTokens); + + if (effectiveWildcards.length == 0) { + return "{{Partition(" + maxMembers + ")}}." + destFromFilter; + } + return "{{Partition(" + maxMembers + "," + wildcardList + ")}}." + destFromFilter; } diff --git a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java index b6c2a59..c81dece 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java @@ -95,25 +95,44 @@ void testValidationMaxMembersZero() { } @Test - void testValidationFilterNoWildcard() { + void testValidationFilterNoWildcardWithWildcardsSpecified() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( 4, "foo.bar", new int[]{1}, 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); - assertTrue(exception.getMessage().contains("filter must contain at least one * wildcard")); + assertTrue(exception.getMessage().contains("number of partitioning wildcards must not be larger than")); } @Test - void testValidationPartitioningWildcardsEmpty() { + void testValidationPartitioningWildcardsEmptyIsValid() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( 4, "foo.*", new int[]{}, 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); - ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); - assertTrue(exception.getMessage().contains("number of partitioning wildcards must be between")); + assertDoesNotThrow(config::validate); + } + + @Test + void testValidationNoFilterIsValid() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, null, new int[]{}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + assertDoesNotThrow(config::validate); + } + + @Test + void testValidationEmptyFilterIsValid() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "", new int[]{}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + assertDoesNotThrow(config::validate); } @Test @@ -124,7 +143,7 @@ void testValidationPartitioningWildcardsTooMany() { ); ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); - assertTrue(exception.getMessage().contains("number of partitioning wildcards must be between")); + assertTrue(exception.getMessage().contains("number of partitioning wildcards must not be larger than")); } @Test @@ -135,7 +154,7 @@ void testValidationPartitioningWildcardsOutOfRange() { ); ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); - assertTrue(exception.getMessage().contains("partitioning wildcard indexes must be greater than 1")); + assertTrue(exception.getMessage().contains("partitioning wildcard indexes must be less than or equal to")); } @Test @@ -232,6 +251,39 @@ void testGetPartitioningTransformDestPartialWildcards() { assertEquals("{{Partition(8,2)}}.a.{{Wildcard(1)}}.b.{{Wildcard(2)}}.c.{{Wildcard(3)}}", dest); } + @Test + void testGetPartitioningTransformDestNoWildcards() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{}, 0, 0, + new ArrayList<>(), new ArrayList<>() + ); + + String dest = config.getPartitioningTransformDest(); + assertEquals("{{Partition(4)}}.foo.{{Wildcard(1)}}", dest); + } + + @Test + void testGetPartitioningTransformDestNoFilter() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, null, new int[]{}, 0, 0, + new ArrayList<>(), new ArrayList<>() + ); + + String dest = config.getPartitioningTransformDest(); + assertEquals("{{Partition(4)}}.>", dest); + } + + @Test + void testGetPartitioningTransformDestEmptyFilter() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "", new int[]{}, 0, 0, + new ArrayList<>(), new ArrayList<>() + ); + + String dest = config.getPartitioningTransformDest(); + assertEquals("{{Partition(4)}}.>", dest); + } + @Test void testJsonSerializationWithMembers() throws JsonParseException { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( diff --git a/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java index 1faa142..13b2586 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java @@ -243,6 +243,56 @@ void testElastic() throws Exception { // Delete elastic consumer group ElasticConsumerGroup.delete(nc, streamName, cgName); + + // --- Test elastic with no partitioning filters (partition on whole subject) --- + String cgName2 = "group2"; + AtomicInteger c3 = new AtomicInteger(0); + AtomicInteger c4 = new AtomicInteger(0); + + // Create elastic consumer group with no filter (null) and empty wildcards + ElasticConsumerGroup.create(nc, streamName, cgName2, 2, null, + new int[]{}, -1, -1); + + // Start consuming on both members + ConsumerGroupConsumeContext cc3 = ElasticConsumerGroup.consume(nc, streamName, cgName2, "m1", msg -> { + c3.incrementAndGet(); + try { + msg.ack(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, config); + + ConsumerGroupConsumeContext cc4 = ElasticConsumerGroup.consume(nc, streamName, cgName2, "m2", msg -> { + c4.incrementAndGet(); + try { + msg.ack(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, config); + + // Add members + ElasticConsumerGroup.addMembers(nc, streamName, cgName2, Arrays.asList("m1", "m2")); + + // Wait for all 30 messages (from previous publishes) to be consumed, split between the 2 members + // The stream has 30 messages total (10 + 10 + 10 from the three publish phases above) + deadline = System.currentTimeMillis() + 10000; + while (c3.get() + c4.get() < 30) { + Thread.sleep(100); + if (System.currentTimeMillis() > deadline) { + fail("timeout no-filter elastic: c3=" + c3.get() + " c4=" + c4.get() + " expected total=30"); + } + } + + assertEquals(30, c3.get() + c4.get()); + + cc3.stop(); + cc4.stop(); + + // Delete elastic consumer group + ElasticConsumerGroup.delete(nc, streamName, cgName2); + nc.close(); } } From ef68eabb0e8ca775262687793716d0b8e0233c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Moyne?= Date: Thu, 12 Mar 2026 13:55:34 +0100 Subject: [PATCH 122/135] Improve elastic consumer group config validation. Fix KV watchers being created with update only. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-Noël Moyne --- .../io/synadia/pcg/ElasticConsumerGroup.java | 2 +- .../pcg/ElasticConsumerGroupConfig.java | 8 +++-- .../io/synadia/pcg/StaticConsumerGroup.java | 2 +- .../synadia/pcg/ElasticConsumerGroupTest.java | 36 +++++++++++++++++-- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java index 29366be..fd80ecc 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java @@ -650,7 +650,7 @@ public void endOfData() { } }; - watchSubscription = kv.watch(key, watcher, KeyValueWatchOption.UPDATES_ONLY); + watchSubscription = kv.watch(key, watcher); } catch (Exception e) { if (!stopped.get()) { diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java index ccc098d..dc057af 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java @@ -198,6 +198,10 @@ public void validate() throws ConsumerGroupException { } } + if (numWildcards == 0 && !">".equals(filterTokens[filterTokens.length - 1])) { + throw new ConsumerGroupException("partitioning filters must have at least one * wildcard or end with > wildcard"); + } + if (partitioningWildcards != null && partitioningWildcards.length > numWildcards) { throw new ConsumerGroupException("the number of partitioning wildcards must not be larger than the total number of * wildcards in the filter"); } @@ -210,8 +214,8 @@ public void validate() throws ConsumerGroupException { } seenWildcards.add(pwc); - if (pwc > numWildcards) { - throw new ConsumerGroupException("partitioning wildcard indexes must be less than or equal to the number of * wildcards in the filter"); + if (pwc > numWildcards || pwc < 1) { + throw new ConsumerGroupException("partitioning wildcard indexes must be between 1 and the number of * wildcards in the filter"); } } } diff --git a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java index 099e7b5..2948370 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/StaticConsumerGroup.java @@ -440,7 +440,7 @@ public void endOfData() { } }; - watchSubscription = kv.watch(key, watcher, KeyValueWatchOption.UPDATES_ONLY); + watchSubscription = kv.watch(key, watcher); } catch (Exception e) { if (!stopped.get()) { diff --git a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java index c81dece..ec06a23 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java @@ -94,6 +94,27 @@ void testValidationMaxMembersZero() { assertTrue(exception.getMessage().contains("max number of members must be >= 1")); } + @Test + void testValidationFilterNoWildcardNoGt() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.bar", new int[]{}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("partitioning filters must have at least one * wildcard or end with > wildcard")); + } + + @Test + void testValidationFilterEndingWithGtIsValid() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.>", new int[]{}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + assertDoesNotThrow(config::validate); + } + @Test void testValidationFilterNoWildcardWithWildcardsSpecified() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( @@ -102,7 +123,7 @@ void testValidationFilterNoWildcardWithWildcardsSpecified() { ); ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); - assertTrue(exception.getMessage().contains("number of partitioning wildcards must not be larger than")); + assertTrue(exception.getMessage().contains("partitioning filters must have at least one * wildcard or end with > wildcard")); } @Test @@ -154,7 +175,18 @@ void testValidationPartitioningWildcardsOutOfRange() { ); ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); - assertTrue(exception.getMessage().contains("partitioning wildcard indexes must be less than or equal to")); + assertTrue(exception.getMessage().contains("partitioning wildcard indexes must be between 1 and")); + } + + @Test + void testValidationPartitioningWildcardsZeroIndex() { + ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( + 4, "foo.*", new int[]{0}, 0, 0, + Arrays.asList("m1", "m2"), new ArrayList<>() + ); + + ConsumerGroupException exception = assertThrows(ConsumerGroupException.class, config::validate); + assertTrue(exception.getMessage().contains("partitioning wildcard indexes must be between 1 and")); } @Test From 061b74d0c52f40d6f81b223adbf7d2cf1f2c8fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Moyne?= Date: Fri, 13 Mar 2026 18:34:37 +0100 Subject: [PATCH 123/135] Go partity: Elastic supports more than one partitioning filters Fix and improve CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-Noël Moyne --- pcgroups-cli/build.gradle | 2 +- .../java/io/synadia/pcg/cli/CliUtils.java | 44 +++++++ .../io/synadia/pcg/cli/ElasticCommands.java | 48 ++++--- .../io/synadia/pcg/cli/PromptHandler.java | 61 ++++++--- .../io/synadia/pcg/cli/StaticCommands.java | 20 +-- .../io/synadia/pcg/ElasticConsumerGroup.java | 66 ++++++---- .../pcg/ElasticConsumerGroupConfig.java | 106 ++++----------- .../java/io/synadia/pcg/PartitionUtils.java | 13 +- .../io/synadia/pcg/PartitioningFilter.java | 121 ++++++++++++++++++ 9 files changed, 325 insertions(+), 156 deletions(-) create mode 100644 pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index 54d7b0f..7680894 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -20,7 +20,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.25.1' implementation 'org.jspecify:jspecify:1.0.0' - implementation 'io.synadia:pcgroups:0.1.0' + implementation 'io.synadia:pcgroups:0.1.1-SNAPSHOT' implementation 'info.picocli:picocli:4.7.5' testImplementation 'io.nats:jnats-server-runner:3.1.0' diff --git a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/CliUtils.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/CliUtils.java index bd355de..f0811ac 100644 --- a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/CliUtils.java +++ b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/CliUtils.java @@ -19,6 +19,7 @@ import io.nats.client.Nats; import io.nats.client.Options; import io.synadia.pcg.MemberMapping; +import io.synadia.pcg.PartitioningFilter; import java.io.*; import java.nio.file.Files; @@ -124,6 +125,49 @@ public static List parseMemberMappings(List mappingArgs) return mappings; } + /** + * Parses partitioning filter arguments in the format "filter:wildcard1,wildcard2" or just "filter". + * Each string in the list represents one PartitioningFilter. + */ + public static List parsePartitioningFilters(List filterArgs) throws IllegalArgumentException { + List filters = new ArrayList<>(); + + if (filterArgs == null || filterArgs.isEmpty()) { + return filters; + } + + for (String arg : filterArgs) { + int colonIndex = arg.indexOf(':'); + if (colonIndex < 0) { + // No wildcards specified + filters.add(new PartitioningFilter(arg, new int[0])); + } else { + String filter = arg.substring(0, colonIndex); + String wildcardsInput = arg.substring(colonIndex + 1); + int[] wildcards = parsePartitioningWildcardsString(wildcardsInput); + filters.add(new PartitioningFilter(filter, wildcards)); + } + } + + return filters; + } + + private static int[] parsePartitioningWildcardsString(String input) throws IllegalArgumentException { + if (input == null || input.isEmpty()) { + return new int[0]; + } + String[] parts = input.split(","); + int[] wildcards = new int[parts.length]; + for (int i = 0; i < parts.length; i++) { + try { + wildcards[i] = Integer.parseInt(parts[i].trim()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("can't parse wildcard index '" + parts[i] + "': not a valid integer"); + } + } + return wildcards; + } + /** * Parses partitioning wildcard indexes from a list of strings. */ diff --git a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java index 5bec79c..b4fe1f9 100644 --- a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java +++ b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/ElasticCommands.java @@ -28,6 +28,7 @@ * Elastic consumer group CLI commands. */ @Command(name = "elastic", description = "Elastic consumer groups mode", + mixinStandardHelpOptions = true, subcommands = { ElasticCommands.Ls.class, ElasticCommands.Info.class, @@ -53,7 +54,7 @@ public Integer call() { return 0; } - @Command(name = "ls", aliases = {"list"}, description = "List elastic consumer groups for a stream") + @Command(name = "ls", aliases = {"list"}, description = "List elastic consumer groups for a stream", mixinStandardHelpOptions = true) static class Ls implements Callable { @ParentCommand private ElasticCommands parent; @@ -74,7 +75,7 @@ public Integer call() { } } - @Command(name = "info", description = "Get elastic consumer group info") + @Command(name = "info", description = "Get elastic consumer group info", mixinStandardHelpOptions = true) static class Info implements Callable { @ParentCommand private ElasticCommands parent; @@ -90,8 +91,16 @@ public Integer call() { try (Connection nc = CliUtils.connect(parent.parent.context)) { ElasticConsumerGroupConfig config = ElasticConsumerGroup.getConfig(nc, streamName, consumerGroupName); - System.out.printf("config: max members=%d, filter=%s, partitioning wildcards %s%n", - config.getMaxMembers(), config.getFilter(), Arrays.toString(config.getPartitioningWildcards())); + System.out.printf("config: max members=%d, max buffered msgs=%d, max buffered bytes=%d%n", config.getMaxMembers(), config.getMaxBufferedMessages(), config.getMaxBufferedBytes()); + + if (config.getPartitioningFilters().isEmpty()) { + System.out.printf("no partitioning filters defined (whole subject used)%n"); + } else { + for (PartitioningFilter pf : config.getPartitioningFilters()) { + System.out.printf("filter=%s, partitioning wildcards %s%n", + pf.getFilter(), Arrays.toString(pf.getPartitioningWildcards())); + } + } if (!config.getMembers().isEmpty()) { System.out.printf("members: %s%n", config.getMembers()); @@ -111,7 +120,7 @@ public Integer call() { } } - @Command(name = "create", description = "Create an elastic partitioned consumer group") + @Command(name = "create", description = "Create an elastic partitioned consumer group", mixinStandardHelpOptions = true) static class Create implements Callable { @ParentCommand private ElasticCommands parent; @@ -125,11 +134,8 @@ static class Create implements Callable { @Parameters(index = "2", description = "Max number of members") int maxMembers; - @Parameters(index = "3", description = "Filter") - String filter; - - @Parameters(index = "4..*", description = "Partitioning wildcard indexes") - List wildcardArgs; + @Option(names = "--filter", description = "Partitioning filter in format 'subject:wildcard1,wildcard2' or just 'subject' (repeatable, omit to use whole subject)", split = "\\|") + List filterArgs; @Option(names = "--max-buffered-msgs", description = "Max number of buffered messages", defaultValue = "0") long maxBufferedMsgs; @@ -140,8 +146,8 @@ static class Create implements Callable { @Override public Integer call() { try (Connection nc = CliUtils.connect(parent.parent.context)) { - int[] wildcards = CliUtils.parsePartitioningWildcards(wildcardArgs); - ElasticConsumerGroup.create(nc, streamName, consumerGroupName, maxMembers, filter, wildcards, maxBufferedMsgs, maxBufferedBytes); + List partitioningFilters = CliUtils.parsePartitioningFilters(filterArgs); + ElasticConsumerGroup.create(nc, streamName, consumerGroupName, maxMembers, partitioningFilters, maxBufferedMsgs, maxBufferedBytes); System.out.println("elastic partitioned consumer group created"); return 0; } catch (Exception e) { @@ -151,7 +157,7 @@ public Integer call() { } } - @Command(name = "delete", aliases = {"rm"}, description = "Delete an elastic partitioned consumer group") + @Command(name = "delete", aliases = {"rm"}, description = "Delete an elastic partitioned consumer group", mixinStandardHelpOptions = true) static class Delete implements Callable { @ParentCommand private ElasticCommands parent; @@ -185,7 +191,7 @@ public Integer call() { } } - @Command(name = "add", description = "Add members to an elastic consumer group") + @Command(name = "add", description = "Add members to an elastic consumer group", mixinStandardHelpOptions = true) static class Add implements Callable { @ParentCommand private ElasticCommands parent; @@ -212,7 +218,7 @@ public Integer call() { } } - @Command(name = "drop", description = "Drop members from an elastic consumer group") + @Command(name = "drop", description = "Drop members from an elastic consumer group", mixinStandardHelpOptions = true) static class Drop implements Callable { @ParentCommand private ElasticCommands parent; @@ -239,7 +245,7 @@ public Integer call() { } } - @Command(name = "create-mapping", aliases = {"cm", "createmapping"}, description = "Create member mappings for an elastic consumer group") + @Command(name = "create-mapping", aliases = {"cm", "createmapping"}, description = "Create member mappings for an elastic consumer group", mixinStandardHelpOptions = true) static class CreateMapping implements Callable { @ParentCommand private ElasticCommands parent; @@ -267,7 +273,7 @@ public Integer call() { } } - @Command(name = "delete-mapping", aliases = {"dm", "deletemapping"}, description = "Delete member mappings for an elastic consumer group") + @Command(name = "delete-mapping", aliases = {"dm", "deletemapping"}, description = "Delete member mappings for an elastic consumer group", mixinStandardHelpOptions = true) static class DeleteMapping implements Callable { @ParentCommand private ElasticCommands parent; @@ -291,7 +297,7 @@ public Integer call() { } } - @Command(name = "member-info", aliases = {"memberinfo", "minfo"}, description = "Get elastic consumer group member info") + @Command(name = "member-info", aliases = {"memberinfo", "minfo"}, description = "Get elastic consumer group member info", mixinStandardHelpOptions = true) static class MemberInfo implements Callable { @ParentCommand private ElasticCommands parent; @@ -330,7 +336,7 @@ public Integer call() { } } - @Command(name = "step-down", aliases = {"stepdown", "sd"}, description = "Initiate a step down for a member") + @Command(name = "step-down", aliases = {"stepdown", "sd"}, description = "Initiate a step down for a member", mixinStandardHelpOptions = true) static class StepDown implements Callable { @ParentCommand private ElasticCommands parent; @@ -357,7 +363,7 @@ public Integer call() { } } - @Command(name = "consume", aliases = {"join"}, description = "Join an elastic partitioned consumer group") + @Command(name = "consume", aliases = {"join"}, description = "Join an elastic partitioned consumer group", mixinStandardHelpOptions = true) static class Consume implements Callable { @ParentCommand private ElasticCommands parent; @@ -417,7 +423,7 @@ public Integer call() { } } - @Command(name = "prompt", description = "Interactive prompt mode") + @Command(name = "prompt", description = "Interactive prompt mode", mixinStandardHelpOptions = true) static class Prompt implements Callable { @ParentCommand private ElasticCommands parent; diff --git a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java index 9279742..7dd7450 100644 --- a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java +++ b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/PromptHandler.java @@ -159,7 +159,7 @@ private void printHelp() { System.out.println(" exit/quit - exit the program"); System.out.println(" list/ls - list partitioned consumer groups"); System.out.println(" info - get partitioned consumer group info"); - System.out.println(" create - create a partitioned consumer group"); + System.out.println(" create - create a partitioned consumer group (interactive, filter and wildcard indexes are optional)"); System.out.println(" delete/rm - delete a partitioned consumer group"); System.out.println(" memberinfo/minfo - get partitioned consumer group member info"); System.out.println(" add [...] - add a member to a partitioned consumer group"); @@ -245,8 +245,15 @@ private void handleInfo(BufferedReader reader, String[] args) { System.out.printf("currently active members: %s%n", activeMembers); } else { ElasticConsumerGroupConfig config = ElasticConsumerGroup.getConfig(nc, currentStream, currentGroup); - System.out.printf("config: max members=%d, filter=%s, partitioning wildcards %s%n", - config.getMaxMembers(), config.getFilter(), Arrays.toString(config.getPartitioningWildcards())); + if (config.getPartitioningFilters().isEmpty()) { + System.out.printf("config: max members=%d, no partitioning filters (whole subject used)%n", + config.getMaxMembers()); + } else { + for (PartitioningFilter pf : config.getPartitioningFilters()) { + System.out.printf("config: max members=%d, filter=%s, partitioning wildcards %s%n", + config.getMaxMembers(), pf.getFilter(), Arrays.toString(pf.getPartitioningWildcards())); + } + } if (!config.getMembers().isEmpty()) { System.out.printf("members: %s%n", config.getMembers()); } else if (!config.getMemberMappings().isEmpty()) { @@ -283,12 +290,11 @@ private void handleCreate(BufferedReader reader) { if (input == null) return; int maxMembers = Integer.parseInt(input.trim()); - System.out.print("filter: "); - input = reader.readLine(); - if (input == null) return; - String filter = input.trim(); - if (isStatic) { + System.out.print("filter (empty for whole subject partitioning): "); + input = reader.readLine(); + if (input == null) return; + String filter = input.trim().isEmpty() ? null : input.trim(); System.out.print("space separated set of members (hit return to set member mappings instead): "); input = reader.readLine(); if (input == null) return; @@ -310,13 +316,29 @@ private void handleCreate(BufferedReader reader) { System.out.println("static partitioned consumer group created"); } } else { - System.out.print("space separated partitioning wildcard indexes: "); - input = reader.readLine(); - if (input == null) return; - String[] pwciArgs = input.trim().split("\\s+"); - int[] wildcards = new int[pwciArgs.length]; - for (int i = 0; i < pwciArgs.length; i++) { - wildcards[i] = Integer.parseInt(pwciArgs[i]); + List partitioningFilters = new ArrayList<>(); + System.out.println("enter partitioning filters (empty filter to finish, or empty first filter for whole subject partitioning):"); + while (true) { + System.out.print(" filter: "); + input = reader.readLine(); + if (input == null) return; + String filter = input.trim(); + if (filter.isEmpty()) break; + + System.out.print(" space separated partitioning wildcard indexes (empty for none): "); + input = reader.readLine(); + if (input == null) return; + int[] wildcards; + if (input.trim().isEmpty()) { + wildcards = new int[0]; + } else { + String[] pwciArgs = input.trim().split("\\s+"); + wildcards = new int[pwciArgs.length]; + for (int i = 0; i < pwciArgs.length; i++) { + wildcards[i] = Integer.parseInt(pwciArgs[i]); + } + } + partitioningFilters.add(new PartitioningFilter(filter, wildcards)); } System.out.print("max buffered messages (0 for no limit): "); @@ -329,7 +351,7 @@ private void handleCreate(BufferedReader reader) { if (input == null) return; long maxBufferedBytes = input.trim().isEmpty() ? 0 : Long.parseLong(input.trim()); - ElasticConsumerGroup.create(nc, currentStream, currentGroup, maxMembers, filter, wildcards, maxBufferedMsgs, maxBufferedBytes); + ElasticConsumerGroup.create(nc, currentStream, currentGroup, maxMembers, partitioningFilters, maxBufferedMsgs, maxBufferedBytes); System.out.println("elastic partitioned consumer group created"); } } catch (Exception e) { @@ -353,6 +375,13 @@ private void handleDelete(BufferedReader reader, String[] args) { currentGroup = args[1]; } + System.out.print("WARNING: this operation will cause all existing consumer members to terminate consuming. Are you sure? (y/n): "); + String confirm = reader.readLine(); + if (confirm == null || !confirm.trim().equalsIgnoreCase("y")) { + System.out.println("Operation canceled"); + return; + } + if (isStatic) { StaticConsumerGroup.delete(nc, currentStream, currentGroup); System.out.println("static consumer group deleted"); diff --git a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java index 5885ae9..6756357 100644 --- a/pcgroups-cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java +++ b/pcgroups-cli/src/main/java/io/synadia/pcg/cli/StaticCommands.java @@ -28,6 +28,7 @@ * Static consumer group CLI commands. */ @Command(name = "static", description = "Static consumer groups mode", + mixinStandardHelpOptions = true, subcommands = { StaticCommands.Ls.class, StaticCommands.Info.class, @@ -49,7 +50,7 @@ public Integer call() { return 0; } - @Command(name = "ls", aliases = {"list"}, description = "List static consumer groups for a stream") + @Command(name = "ls", aliases = {"list"}, description = "List static consumer groups for a stream", mixinStandardHelpOptions = true) static class Ls implements Callable { @ParentCommand private StaticCommands parent; @@ -70,7 +71,7 @@ public Integer call() { } } - @Command(name = "info", description = "Get static consumer group info") + @Command(name = "info", description = "Get static consumer group info", mixinStandardHelpOptions = true) static class Info implements Callable { @ParentCommand private StaticCommands parent; @@ -107,6 +108,7 @@ public Integer call() { } @Command(name = "create", description = "Create a static partitioned consumer group", + mixinStandardHelpOptions = true, subcommands = {StaticCommands.CreateBalanced.class, StaticCommands.CreateMapped.class}) static class Create implements Callable { @ParentCommand @@ -119,7 +121,7 @@ public Integer call() { } } - @Command(name = "balanced", description = "Create a static consumer group with balanced members") + @Command(name = "balanced", description = "Create a static consumer group with balanced members", mixinStandardHelpOptions = true) static class CreateBalanced implements Callable { @ParentCommand private Create createParent; @@ -152,7 +154,7 @@ public Integer call() { } } - @Command(name = "mapped", description = "Create a static consumer group with member mappings") + @Command(name = "mapped", description = "Create a static consumer group with member mappings", mixinStandardHelpOptions = true) static class CreateMapped implements Callable { @ParentCommand private Create createParent; @@ -186,7 +188,7 @@ public Integer call() { } } - @Command(name = "delete", aliases = {"rm"}, description = "Delete a static partitioned consumer group") + @Command(name = "delete", aliases = {"rm"}, description = "Delete a static partitioned consumer group", mixinStandardHelpOptions = true) static class Delete implements Callable { @ParentCommand private StaticCommands parent; @@ -220,7 +222,7 @@ public Integer call() { } } - @Command(name = "member-info", aliases = {"memberinfo", "minfo"}, description = "Get static consumer group member info") + @Command(name = "member-info", aliases = {"memberinfo", "minfo"}, description = "Get static consumer group member info", mixinStandardHelpOptions = true) static class MemberInfo implements Callable { @ParentCommand private StaticCommands parent; @@ -258,7 +260,7 @@ public Integer call() { } } - @Command(name = "step-down", aliases = {"stepdown", "sd"}, description = "Initiate a step down for a member") + @Command(name = "step-down", aliases = {"stepdown", "sd"}, description = "Initiate a step down for a member", mixinStandardHelpOptions = true) static class StepDown implements Callable { @ParentCommand private StaticCommands parent; @@ -285,7 +287,7 @@ public Integer call() { } } - @Command(name = "consume", aliases = {"join"}, description = "Join a static partitioned consumer group") + @Command(name = "consume", aliases = {"join"}, description = "Join a static partitioned consumer group", mixinStandardHelpOptions = true) static class Consume implements Callable { @ParentCommand private StaticCommands parent; @@ -345,7 +347,7 @@ public Integer call() { } } - @Command(name = "prompt", description = "Interactive prompt mode") + @Command(name = "prompt", description = "Interactive prompt mode", mixinStandardHelpOptions = true) static class Prompt implements Callable { @ParentCommand private StaticCommands parent; diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java index fd80ecc..fd3bcb5 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java @@ -50,19 +50,18 @@ private ElasticConsumerGroup() { * @param streamName Name of the source stream * @param consumerGroupName Name of the consumer group * @param maxMembers Maximum number of members (partitions) - * @param filter Subject filter with wildcards - * @param partitioningWildcards Indexes of wildcards to use for partitioning - * @param maxBufferedMessages Max messages in work queue (0 for unlimited) + * @param partitioningFilters List of partitioning filters + * @param maxBufferedMessages Max messages in work queue (0 for unlimited) * @param maxBufferedBytes Max bytes in work queue (0 for unlimited) * @return The created configuration */ public static ElasticConsumerGroupConfig create(Connection nc, String streamName, String consumerGroupName, - int maxMembers, String filter, int[] partitioningWildcards, + int maxMembers, List partitioningFilters, long maxBufferedMessages, long maxBufferedBytes) throws ConsumerGroupException, IOException, JetStreamApiException, InterruptedException { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - maxMembers, filter, partitioningWildcards, maxBufferedMessages, maxBufferedBytes, + maxMembers, partitioningFilters, maxBufferedMessages, maxBufferedBytes, new ArrayList<>(), new ArrayList<>()); config.validate(); @@ -95,10 +94,9 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName ElasticConsumerGroupConfig existingConfig = ElasticConsumerGroupConfig.instance(kv.get(key)); if (existingConfig != null) { if (existingConfig.getMaxMembers() != maxMembers || - !Objects.equals(existingConfig.getFilter(), filter) || + !Objects.equals(existingConfig.getPartitioningFilters(), partitioningFilters) || existingConfig.getMaxBufferedMessages() != maxBufferedMessages || - existingConfig.getMaxBufferedBytes() != maxBufferedBytes || - !Arrays.equals(existingConfig.getPartitioningWildcards(), partitioningWildcards)) { + existingConfig.getMaxBufferedBytes() != maxBufferedBytes) { throw new ConsumerGroupException( "the existing elastic consumer group config can not be updated to the requested one, " + "please delete the existing elastic consumer group and create a new one"); @@ -111,8 +109,6 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName // Create the work queue stream with subject transform String workQueueStreamName = composeCGSName(streamName, consumerGroupName); - String effectiveFilter = (filter != null && !filter.isEmpty()) ? filter : ">"; - String filterDest = getPartitioningTransformDest(config); StreamConfiguration.Builder scBuilder = StreamConfiguration.builder() .name(workQueueStreamName) @@ -129,14 +125,26 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName scBuilder.maxBytes(maxBufferedBytes); } - // Add source with subject transform + // Add source with subject transforms + List subjectTransforms = new ArrayList<>(); + if (partitioningFilters != null && !partitioningFilters.isEmpty()) { + for (PartitioningFilter pf : partitioningFilters) { + subjectTransforms.add(SubjectTransform.builder() + .source(pf.getFilter()) + .destination(getPartitioningTransformDest(pf, maxMembers)) + .build()); + } + } else { + subjectTransforms.add(SubjectTransform.builder() + .source(">") + .destination(getPartitioningTransformDest(new PartitioningFilter(">", new int[0]), maxMembers)) + .build()); + } + scBuilder.addSource(Source.builder() .sourceName(streamName) .startSeq(0) - .subjectTransforms(SubjectTransform.builder() - .source(effectiveFilter) - .destination(filterDest) - .build()) + .subjectTransforms(subjectTransforms.toArray(new SubjectTransform[0])) .build()); try { @@ -513,8 +521,17 @@ public static ElasticConsumerGroupConfig getConfig(Connection nc, String streamN * Returns the list of partition filters for a given member based on the config. */ public static List getPartitionFilters(ElasticConsumerGroupConfig config, String memberName) { - return PartitionUtils.generatePartitionFilters( - config.getMembers(), config.getMaxMembers(), config.getMemberMappings(), memberName); + List filters = new ArrayList<>(); + if (!config.getPartitioningFilters().isEmpty()) { + for (PartitioningFilter pf : config.getPartitioningFilters()) { + filters.addAll(PartitionUtils.generatePartitionFilters( + config.getMembers(), config.getMaxMembers(), config.getMemberMappings(), memberName, pf.getFilter())); + } + } else { + filters.addAll(PartitionUtils.generatePartitionFilters( + config.getMembers(), config.getMaxMembers(), config.getMemberMappings(), memberName)); + } + return filters; } private static ElasticConsumerGroupConfig getConfigFromKV(KeyValue kv, String streamName, String consumerGroupName) @@ -533,9 +550,9 @@ private static ElasticConsumerGroupConfig getConfigFromKV(KeyValue kv, String st return config; } - private static String getPartitioningTransformDest(ElasticConsumerGroupConfig config) { - String effectiveFilter = (config.getFilter() != null && !config.getFilter().isEmpty()) ? config.getFilter() : ">"; - int[] wildcards = config.getPartitioningWildcards(); + private static String getPartitioningTransformDest(PartitioningFilter pf, int maxMembers) { + String effectiveFilter = (pf.getFilter() != null && !pf.getFilter().isEmpty()) ? pf.getFilter() : ">"; + int[] wildcards = pf.getPartitioningWildcards(); StringBuilder wildcardList = new StringBuilder(); for (int i = 0; i < wildcards.length; i++) { @@ -555,10 +572,10 @@ private static String getPartitioningTransformDest(ElasticConsumerGroupConfig co String destFromFilter = String.join(".", filterTokens); if (wildcards.length == 0) { - return "{{Partition(" + config.getMaxMembers() + ")}}." + destFromFilter; + return "{{Partition(" + maxMembers + ")}}." + destFromFilter; } - return "{{Partition(" + config.getMaxMembers() + "," + wildcardList + ")}}." + destFromFilter; + return "{{Partition(" + maxMembers + "," + wildcardList + ")}}." + destFromFilter; } /** @@ -723,10 +740,9 @@ private void processWatcherUpdate(KeyValueEntry entry) { // Check if critical config changed (immutable fields) if (newConfig.getMaxMembers() != config.getMaxMembers() || - !Objects.equals(newConfig.getFilter(), config.getFilter()) || + !Objects.equals(newConfig.getPartitioningFilters(), config.getPartitioningFilters()) || newConfig.getMaxBufferedMessages() != config.getMaxBufferedMessages() || - newConfig.getMaxBufferedBytes() != config.getMaxBufferedBytes() || - !Arrays.equals(newConfig.getPartitioningWildcards(), config.getPartitioningWildcards())) { + newConfig.getMaxBufferedBytes() != config.getMaxBufferedBytes()) { stopConsuming(); stopped.set(true); doneFuture.completeExceptionally( diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java index dc057af..16c2e9e 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroupConfig.java @@ -30,16 +30,14 @@ */ public class ElasticConsumerGroupConfig implements JsonSerializable { static final String MAX_MEMBERS = "max_members"; - static final String FILTER = "filter"; - static final String PARTITIONING_WILDCARDS = "partitioning_wildcards"; + static final String PARTITIONING_FILTERS = "partitioning_filters"; static final String MAX_BUFFERED_MSG = "max_buffered_msg"; static final String MAX_BUFFERED_BYTES = "max_buffered_bytes"; static final String MEMBERS = "members"; static final String MEMBER_MAPPINGS = "member_mappings"; private int maxMembers; - private String filter; - private int[] partitioningWildcards; + private List partitioningFilters; private long maxBufferedMessages; private long maxBufferedBytes; private List members; @@ -67,17 +65,16 @@ public static ElasticConsumerGroupConfig instance(byte @NonNull[] json) throws J } public ElasticConsumerGroupConfig() { - this.partitioningWildcards = new int[0]; + this.partitioningFilters = new ArrayList<>(); this.members = new ArrayList<>(); this.memberMappings = new ArrayList<>(); } - public ElasticConsumerGroupConfig(int maxMembers, String filter, int[] partitioningWildcards, + public ElasticConsumerGroupConfig(int maxMembers, List partitioningFilters, long maxBufferedMessages, long maxBufferedBytes, List members, List memberMappings) { this.maxMembers = maxMembers; - this.filter = filter; - this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + this.partitioningFilters = partitioningFilters == null ? new ArrayList<>() : new ArrayList<>(partitioningFilters); this.maxBufferedMessages = maxBufferedMessages; this.maxBufferedBytes = maxBufferedBytes; this.members = members == null ? new ArrayList<>() : new ArrayList<>(members); @@ -86,13 +83,7 @@ public ElasticConsumerGroupConfig(int maxMembers, String filter, int[] partition public ElasticConsumerGroupConfig(JsonValue jv) { this.maxMembers = JsonValueUtils.readInteger(jv, MAX_MEMBERS, 0); - this.filter = JsonValueUtils.readString(jv, FILTER); - List integers = read(jv, PARTITIONING_WILDCARDS, v -> listOf(v, JsonValueUtils::getInteger)); - this.partitioningWildcards = new int[integers.size()]; - for (int x = 0; x < integers.size(); x++) { - Integer i = integers.get(x); - this.partitioningWildcards[x] = i == null ? 0 : i; - } + this.partitioningFilters = PartitioningFilter.listOfOrEmptyList(readValue(jv, PARTITIONING_FILTERS)); this.maxBufferedMessages = JsonValueUtils.readLong(jv, MAX_BUFFERED_MSG, 0); this.maxBufferedBytes = JsonValueUtils.readLong(jv, MAX_BUFFERED_BYTES, 0); this.members = JsonValueUtils.readStringList(jv, MEMBERS); @@ -107,20 +98,12 @@ public void setMaxMembers(int maxMembers) { this.maxMembers = maxMembers; } - public String getFilter() { - return filter; - } - - public void setFilter(String filter) { - this.filter = filter; + public List getPartitioningFilters() { + return new ArrayList<>(partitioningFilters); } - public int[] getPartitioningWildcards() { - return partitioningWildcards.clone(); - } - - public void setPartitioningWildcards(int[] partitioningWildcards) { - this.partitioningWildcards = partitioningWildcards == null ? new int[0] : partitioningWildcards.clone(); + public void setPartitioningFilters(List partitioningFilters) { + this.partitioningFilters = partitioningFilters == null ? new ArrayList<>() : new ArrayList<>(partitioningFilters); } public long getMaxBufferedMessages() { @@ -188,9 +171,13 @@ public void validate() throws ConsumerGroupException { throw new ConsumerGroupException("the max number of members must be >= 1"); } - // Validate filter and partitioning wildcards - if (filter != null && !filter.isEmpty()) { - String[] filterTokens = filter.split("\\."); + // Validate partitioning filters + for (PartitioningFilter pf : partitioningFilters) { + if (pf.getFilter() == null || pf.getFilter().isEmpty()) { + throw new ConsumerGroupException("partitioning filters must have a non-empty filter"); + } + + String[] filterTokens = pf.getFilter().split("\\."); int numWildcards = 0; for (String token : filterTokens) { if ("*".equals(token)) { @@ -202,13 +189,14 @@ public void validate() throws ConsumerGroupException { throw new ConsumerGroupException("partitioning filters must have at least one * wildcard or end with > wildcard"); } - if (partitioningWildcards != null && partitioningWildcards.length > numWildcards) { + int[] wildcards = pf.getPartitioningWildcards(); + if (wildcards != null && wildcards.length > numWildcards) { throw new ConsumerGroupException("the number of partitioning wildcards must not be larger than the total number of * wildcards in the filter"); } Set seenWildcards = new HashSet<>(); - if (partitioningWildcards != null) { - for (int pwc : partitioningWildcards) { + if (wildcards != null) { + for (int pwc : wildcards) { if (seenWildcards.contains(pwc)) { throw new ConsumerGroupException("partitioning wildcard indexes must be unique"); } @@ -262,52 +250,12 @@ public void validate() throws ConsumerGroupException { } } - /** - * Generates the subject transform destination for partitioning. - */ - public String getPartitioningTransformDest() { - String effectiveFilter = (filter != null && !filter.isEmpty()) ? filter : ">"; - int[] effectiveWildcards = (partitioningWildcards != null) ? partitioningWildcards : new int[0]; - - StringBuilder wildcardList = new StringBuilder(); - for (int i = 0; i < effectiveWildcards.length; i++) { - if (i > 0) { - wildcardList.append(","); - } - wildcardList.append(effectiveWildcards[i]); - } - - String[] filterTokens = effectiveFilter.split("\\."); - int cwIndex = 1; - for (int i = 0; i < filterTokens.length; i++) { - if ("*".equals(filterTokens[i])) { - filterTokens[i] = "{{Wildcard(" + cwIndex + ")}}"; - cwIndex++; - } - } - - String destFromFilter = String.join(".", filterTokens); - - if (effectiveWildcards.length == 0) { - return "{{Partition(" + maxMembers + ")}}." + destFromFilter; - } - - return "{{Partition(" + maxMembers + "," + wildcardList + ")}}." + destFromFilter; - } - @Override @NonNull public String toJson() { StringBuilder sb = beginJson(); addField(sb, MAX_MEMBERS, maxMembers); - addField(sb, FILTER, filter); - if (partitioningWildcards.length > 0) { - List integers = new ArrayList<>(partitioningWildcards.length); - for (int i : partitioningWildcards) { - integers.add(i); - } - _addList(sb, PARTITIONING_WILDCARDS, integers, StringBuilder::append); - } + addJsons(sb, PARTITIONING_FILTERS, partitioningFilters); addField(sb, MAX_BUFFERED_MSG, maxBufferedMessages); addField(sb, MAX_BUFFERED_BYTES, maxBufferedBytes); addStrings(sb, MEMBERS, members); @@ -323,25 +271,21 @@ public boolean equals(Object o) { return maxMembers == that.maxMembers && maxBufferedMessages == that.maxBufferedMessages && maxBufferedBytes == that.maxBufferedBytes && - Objects.equals(filter, that.filter) && - Arrays.equals(partitioningWildcards, that.partitioningWildcards) && + Objects.equals(partitioningFilters, that.partitioningFilters) && Objects.equals(members, that.members) && Objects.equals(memberMappings, that.memberMappings); } @Override public int hashCode() { - int result = Objects.hash(maxMembers, filter, maxBufferedMessages, maxBufferedBytes, members, memberMappings); - result = 31 * result + Arrays.hashCode(partitioningWildcards); - return result; + return Objects.hash(maxMembers, partitioningFilters, maxBufferedMessages, maxBufferedBytes, members, memberMappings); } @Override public String toString() { return "ElasticConsumerGroupConfig{" + "maxMembers=" + maxMembers + - ", filter='" + filter + '\'' + - ", partitioningWildcards=" + Arrays.toString(partitioningWildcards) + + ", partitioningFilters=" + partitioningFilters + ", maxBufferedMessages=" + maxBufferedMessages + ", maxBufferedBytes=" + maxBufferedBytes + ", members=" + members + diff --git a/pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java b/pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java index ddb96e1..dddf95a 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java +++ b/pcgroups/src/main/java/io/synadia/pcg/PartitionUtils.java @@ -75,6 +75,13 @@ public static String composeStaticConsumerName(String consumerGroupName, String */ public static List generatePartitionFilters(List members, int maxMembers, List memberMappings, String memberName) { + return generatePartitionFilters(members, maxMembers, memberMappings, memberName, ">"); + } + + public static List generatePartitionFilters(List members, int maxMembers, + List memberMappings, String memberName, + String filter) { + String effectiveFilter = (filter != null && !filter.isEmpty()) ? filter : ">"; if (members != null && !members.isEmpty()) { // Deduplicate and sort members List sortedMembers = members.stream() @@ -99,12 +106,12 @@ public static List generatePartitionFilters(List members, int ma if (i < (numMembers * numPer)) { if (sortedMembers.get(memberIndex % numMembers).equals(memberName)) { - myFilters.add(i + ".>"); + myFilters.add(i + "." + effectiveFilter); } } else { // Remainder if the number of partitions is not a multiple of the number of members if (sortedMembers.get((i - (numMembers * numPer)) % numMembers).equals(memberName)) { - myFilters.add(i + ".>"); + myFilters.add(i + "." + effectiveFilter); } } } @@ -118,7 +125,7 @@ public static List generatePartitionFilters(List members, int ma for (MemberMapping mapping : memberMappings) { if (mapping.getMember().equals(memberName)) { for (int pn : mapping.getPartitions()) { - myFilters.add(pn + ".>"); + myFilters.add(pn + "." + effectiveFilter); } } } diff --git a/pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java b/pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java new file mode 100644 index 0000000..436e9ee --- /dev/null +++ b/pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java @@ -0,0 +1,121 @@ +// Copyright 2024-2025 Synadia Communications Inc. +// Licensed 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 io.synadia.pcg; + +import io.nats.client.support.JsonSerializable; +import io.nats.client.support.JsonValue; +import io.nats.client.support.JsonValueUtils; +import org.jspecify.annotations.NonNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import static io.nats.client.support.JsonUtils.*; +import static io.nats.client.support.JsonValueUtils.*; + +/** + * Represents a partitioning filter with its associated wildcard indexes. + * JSON structure must be compatible with the Go version. + */ +public class PartitioningFilter implements JsonSerializable { + static final String FILTER = "filter"; + static final String PARTITIONING_WILDCARDS = "partitioning_wildcards"; + + private String filter; + private int[] partitioningWildcards; + + static List listOfOrEmptyList(JsonValue jv) { + return JsonValueUtils.listOf(jv, PartitioningFilter::new); + } + + public PartitioningFilter() { + this.partitioningWildcards = new int[0]; + } + + public PartitioningFilter(String filter, int[] partitioningWildcards) { + this.filter = filter; + this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + } + + public PartitioningFilter(JsonValue jv) { + this.filter = readString(jv, FILTER); + List integers = read(jv, PARTITIONING_WILDCARDS, v -> listOf(v, JsonValueUtils::getInteger)); + if (integers == null || integers.isEmpty()) { + this.partitioningWildcards = new int[0]; + } else { + this.partitioningWildcards = new int[integers.size()]; + for (int x = 0; x < integers.size(); x++) { + Integer i = integers.get(x); + this.partitioningWildcards[x] = i == null ? 0 : i; + } + } + } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public int[] getPartitioningWildcards() { + return partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + } + + public void setPartitioningWildcards(int[] partitioningWildcards) { + this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; + } + + @Override + @NonNull + public String toJson() { + StringBuilder sb = beginJson(); + addField(sb, FILTER, filter); + if (partitioningWildcards.length > 0) { + List integers = new ArrayList<>(partitioningWildcards.length); + for (int i : partitioningWildcards) { + integers.add(i); + } + _addList(sb, PARTITIONING_WILDCARDS, integers, StringBuilder::append); + } + return endJson(sb).toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PartitioningFilter that = (PartitioningFilter) o; + return Objects.equals(filter, that.filter) && + Arrays.equals(partitioningWildcards, that.partitioningWildcards); + } + + @Override + public int hashCode() { + int result = Objects.hash(filter); + result = 31 * result + Arrays.hashCode(partitioningWildcards); + return result; + } + + @Override + public String toString() { + return "PartitioningFilter{" + + "filter='" + filter + '\'' + + ", partitioningWildcards=" + Arrays.toString(partitioningWildcards) + + '}'; + } +} From afff3b5da6603a0c37a42dc37a394f59b0826797 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 13 Mar 2026 14:39:26 -0400 Subject: [PATCH 124/135] Converting PCGroups and CLI to root and subproject for easier building --- .github/workflows/pcgcli.yml | 43 ++---- pcgroups-cli/README.md | 9 -- pcgroups-cli/build.gradle | 7 +- pcgroups-cli/gradle/libs.versions.toml | 12 -- .../gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 45457 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- pcgroups-cli/pom.xml | 141 ------------------ pcgroups-cli/settings.gradle | 13 -- pcgroups/build.gradle | 2 +- pcgroups/settings.gradle | 3 + 10 files changed, 18 insertions(+), 214 deletions(-) delete mode 100644 pcgroups-cli/gradle/libs.versions.toml delete mode 100644 pcgroups-cli/pom.xml delete mode 100644 pcgroups-cli/settings.gradle diff --git a/.github/workflows/pcgcli.yml b/.github/workflows/pcgcli.yml index 569c145..bf3fdc2 100644 --- a/.github/workflows/pcgcli.yml +++ b/.github/workflows/pcgcli.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ./pcgroups-cli + working-directory: ./pcgroups steps: - name: Set up JDK uses: actions/setup-java@v5 @@ -24,36 +24,15 @@ jobs: - name: Check out code uses: actions/checkout@v4 - name: Build with Gradle - run: chmod +x gradlew && ./gradlew clean dist - - name: Build with Maven - run: mvn clean package + run: chmod +x gradlew && ./gradlew :pcgroups-cli:clean :pcgroups-cli:dist - name: Validate artifacts were created run: | - FILE_PATH="build/cg.jar" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi - FILE_PATH="build/cg.zip" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi - FILE_PATH="build/cg.tar" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi - FILE_PATH="target/cg.jar" - if [ -f "$FILE_PATH" ]; then - echo "Validation successful: $FILE_PATH was created." - else - echo "Validation failed: $FILE_PATH was not found." - exit 1 # Fails the workflow step - fi + CLI_BUILD="../pcgroups-cli/build" + for f in cg.jar cg.zip cg.tar; do + if [ -f "$CLI_BUILD/$f" ]; then + echo "Validation successful: $CLI_BUILD/$f was created." + else + echo "Validation failed: $CLI_BUILD/$f was not found." + exit 1 + fi + done diff --git a/pcgroups-cli/README.md b/pcgroups-cli/README.md index 2fb749c..4f2c02b 100644 --- a/pcgroups-cli/README.md +++ b/pcgroups-cli/README.md @@ -66,15 +66,6 @@ Commands: ``` ## Building from Source -The project contains both a Maven pom.xml file and a Gradle project, -and both are configured to build an executable Java jar named `cg.jar` - -### Maven -``` -mvn clean package -``` - -will build the `cg.jar` in the `target` folder ### Gradle ``` diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index 7680894..2b1079c 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -3,7 +3,7 @@ plugins { id 'com.github.johnrengelman.shadow' version '8.1.1' // Apply the shadow plugin } -version = "0.1.0" +version = "0.2.0" def originalShadow = 'pcg-cli-' + version + '-all.jar' java { @@ -12,15 +12,12 @@ java { repositories { mavenCentral() - mavenLocal() maven { url="https://repo1.maven.org/maven2/" } maven { url="https://central.sonatype.com/repository/maven-snapshots" } } dependencies { - implementation 'io.nats:jnats:2.25.1' - implementation 'org.jspecify:jspecify:1.0.0' - implementation 'io.synadia:pcgroups:0.1.1-SNAPSHOT' + implementation project(':') // ':' means root project which is 'pcgroups' implementation 'info.picocli:picocli:4.7.5' testImplementation 'io.nats:jnats-server-runner:3.1.0' diff --git a/pcgroups-cli/gradle/libs.versions.toml b/pcgroups-cli/gradle/libs.versions.toml deleted file mode 100644 index 2cfe86a..0000000 --- a/pcgroups-cli/gradle/libs.versions.toml +++ /dev/null @@ -1,12 +0,0 @@ -# This file was generated by the Gradle 'init' task. -# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format - -[versions] -commons-math3 = "3.6.1" -guava = "33.4.5-jre" -junit-jupiter = "5.12.1" - -[libraries] -commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } -guava = { module = "com.google.guava:guava", version.ref = "guava" } -junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/pcgroups-cli/gradle/wrapper/gradle-wrapper.jar b/pcgroups-cli/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..8bdaf60c75ab801e22807dde59e12a8735a34077 100644 GIT binary patch delta 37256 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 delta 35551 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 diff --git a/pcgroups-cli/gradle/wrapper/gradle-wrapper.properties b/pcgroups-cli/gradle/wrapper/gradle-wrapper.properties index ca025c8..2a84e18 100644 --- a/pcgroups-cli/gradle/wrapper/gradle-wrapper.properties +++ b/pcgroups-cli/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/pcgroups-cli/pom.xml b/pcgroups-cli/pom.xml deleted file mode 100644 index c5ff3c3..0000000 --- a/pcgroups-cli/pom.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - 4.0.0 - - io.synadia - pcg-cli - 0.1.0 - jar - - Partitioned Consumer Groups CLI - CLI tool for managing NATS JetStream Partitioned Consumer Groups - - - 1.8 - UTF-8 - 0.1.0-SNAPSHOT - 4.7.5 - 2.25.1 - - - - - sonatype releases - https://repo1.maven.org/maven2/ - - true - - - - sonatype snapshots - https://central.sonatype.com/repository/maven-snapshots - - true - - - - - - - - io.synadia - pcgroups - ${pcgroups.version} - - - - - io.nats - jnats - ${jnats.version} - - - - - info.picocli - picocli - ${picocli.version} - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - 11 - 11 - - - info.picocli - picocli-codegen - ${picocli.version} - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.5.1 - - - package - - shade - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - io.synadia.pcg.cli.CgCommand - - - false - cg - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - - io.synadia.pcg.cli.CgCommand - - - - - - - diff --git a/pcgroups-cli/settings.gradle b/pcgroups-cli/settings.gradle deleted file mode 100644 index ce87931..0000000 --- a/pcgroups-cli/settings.gradle +++ /dev/null @@ -1,13 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - mavenCentral() - maven { url="https://repo1.maven.org/maven2/" } - maven { url="https://central.sonatype.com/repository/maven-snapshots/" } - maven { url="https://plugins.gradle.org/m2/" } - } - plugins { - id("biz.aQute.bnd.builder") version "7.1.0" - } -} -rootProject.name = 'pcg-cli' diff --git a/pcgroups/build.gradle b/pcgroups/build.gradle index 225e274..d672d1e 100644 --- a/pcgroups/build.gradle +++ b/pcgroups/build.gradle @@ -11,7 +11,7 @@ plugins { id("signing") } -def jarVersion = "0.1.1" +def jarVersion = "0.2.0" group = 'io.synadia' def isRelease = System.getenv("BUILD_EVENT") == "release" diff --git a/pcgroups/settings.gradle b/pcgroups/settings.gradle index a831682..6d41f45 100644 --- a/pcgroups/settings.gradle +++ b/pcgroups/settings.gradle @@ -11,3 +11,6 @@ pluginManagement { } } rootProject.name = 'pcgroups' + +include 'pcgroups-cli' +project(':pcgroups-cli').projectDir = file('../pcgroups-cli') From 86c8892b545781657ebbb5102f167924b19df594 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 13 Mar 2026 14:46:59 -0400 Subject: [PATCH 125/135] Converting PCGroups and CLI to root and subproject for easier building --- pcgroups-cli/gradlew | 251 --------------------------------------- pcgroups-cli/gradlew.bat | 94 --------------- 2 files changed, 345 deletions(-) delete mode 100644 pcgroups-cli/gradlew delete mode 100644 pcgroups-cli/gradlew.bat diff --git a/pcgroups-cli/gradlew b/pcgroups-cli/gradlew deleted file mode 100644 index 23d15a9..0000000 --- a/pcgroups-cli/gradlew +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed 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 -# -# https://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. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH="\\\"\\\"" - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/pcgroups-cli/gradlew.bat b/pcgroups-cli/gradlew.bat deleted file mode 100644 index db3a6ac..0000000 --- a/pcgroups-cli/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega From e08f5ade53bdfe1cdca5c22622052e05e27cf43b Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 13 Mar 2026 15:50:37 -0400 Subject: [PATCH 126/135] tuning build --- pcgroups-cli/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index 2b1079c..9946add 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -4,7 +4,7 @@ plugins { } version = "0.2.0" -def originalShadow = 'pcg-cli-' + version + '-all.jar' +def originalShadow = 'pcgroups-cli-' + version + '-all.jar' java { sourceCompatibility = JavaVersion.VERSION_1_8 From bd9b7dde29152a99ce033660d3110d5afb92a7c2 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 13 Mar 2026 16:41:40 -0400 Subject: [PATCH 127/135] updating tests --- .../io/synadia/pcg/ElasticConsumerGroup.java | 33 +--- .../io/synadia/pcg/PartitioningFilter.java | 35 ++++ .../synadia/pcg/ElasticConsumerGroupTest.java | 163 ++++++++++-------- .../java/io/synadia/pcg/IntegrationTest.java | 8 +- 4 files changed, 130 insertions(+), 109 deletions(-) diff --git a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java index fd3bcb5..5ef23ca 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java +++ b/pcgroups/src/main/java/io/synadia/pcg/ElasticConsumerGroup.java @@ -30,6 +30,7 @@ import java.util.logging.Logger; import static io.synadia.pcg.PartitionUtils.*; +import static io.synadia.pcg.PartitioningFilter.EVERYTHING; /** * Elastic consumer group implementation. @@ -131,13 +132,13 @@ public static ElasticConsumerGroupConfig create(Connection nc, String streamName for (PartitioningFilter pf : partitioningFilters) { subjectTransforms.add(SubjectTransform.builder() .source(pf.getFilter()) - .destination(getPartitioningTransformDest(pf, maxMembers)) + .destination(pf.getPartitioningTransformDest(maxMembers)) .build()); } } else { subjectTransforms.add(SubjectTransform.builder() .source(">") - .destination(getPartitioningTransformDest(new PartitioningFilter(">", new int[0]), maxMembers)) + .destination(EVERYTHING.getPartitioningTransformDest(maxMembers)) .build()); } @@ -550,34 +551,6 @@ private static ElasticConsumerGroupConfig getConfigFromKV(KeyValue kv, String st return config; } - private static String getPartitioningTransformDest(PartitioningFilter pf, int maxMembers) { - String effectiveFilter = (pf.getFilter() != null && !pf.getFilter().isEmpty()) ? pf.getFilter() : ">"; - int[] wildcards = pf.getPartitioningWildcards(); - - StringBuilder wildcardList = new StringBuilder(); - for (int i = 0; i < wildcards.length; i++) { - if (i > 0) wildcardList.append(","); - wildcardList.append(wildcards[i]); - } - - String[] filterTokens = effectiveFilter.split("\\."); - int cwIndex = 1; - for (int i = 0; i < filterTokens.length; i++) { - if (filterTokens[i].equals("*")) { - filterTokens[i] = "{{Wildcard(" + cwIndex + ")}}"; - cwIndex++; - } - } - - String destFromFilter = String.join(".", filterTokens); - - if (wildcards.length == 0) { - return "{{Partition(" + maxMembers + ")}}." + destFromFilter; - } - - return "{{Partition(" + maxMembers + "," + wildcardList + ")}}." + destFromFilter; - } - /** * Composes the Consumer Group Stream Name. */ diff --git a/pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java b/pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java index 436e9ee..8e9fbb7 100644 --- a/pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java +++ b/pcgroups/src/main/java/io/synadia/pcg/PartitioningFilter.java @@ -25,6 +25,7 @@ import static io.nats.client.support.JsonUtils.*; import static io.nats.client.support.JsonValueUtils.*; +import static io.nats.client.support.JsonValueUtils.readString; /** * Represents a partitioning filter with its associated wildcard indexes. @@ -34,6 +35,8 @@ public class PartitioningFilter implements JsonSerializable { static final String FILTER = "filter"; static final String PARTITIONING_WILDCARDS = "partitioning_wildcards"; + public static PartitioningFilter EVERYTHING = new PartitioningFilter(">", new int[0]); + private String filter; private int[] partitioningWildcards; @@ -45,6 +48,10 @@ public PartitioningFilter() { this.partitioningWildcards = new int[0]; } + public PartitioningFilter(String filter) { + this(filter, new int[0]); + } + public PartitioningFilter(String filter, int[] partitioningWildcards) { this.filter = filter; this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; @@ -80,6 +87,34 @@ public void setPartitioningWildcards(int[] partitioningWildcards) { this.partitioningWildcards = partitioningWildcards != null ? partitioningWildcards.clone() : new int[0]; } + public String getPartitioningTransformDest(int maxMembers) { + String effectiveFilter = (getFilter() != null && !getFilter().isEmpty()) ? getFilter() : ">"; + int[] wildcards = getPartitioningWildcards(); + + StringBuilder wildcardList = new StringBuilder(); + for (int i = 0; i < wildcards.length; i++) { + if (i > 0) wildcardList.append(","); + wildcardList.append(wildcards[i]); + } + + String[] filterTokens = effectiveFilter.split("\\."); + int cwIndex = 1; + for (int i = 0; i < filterTokens.length; i++) { + if (filterTokens[i].equals("*")) { + filterTokens[i] = "{{Wildcard(" + cwIndex + ")}}"; + cwIndex++; + } + } + + String destFromFilter = String.join(".", filterTokens); + + if (wildcards.length == 0) { + return "{{Partition(" + maxMembers + ")}}." + destFromFilter; + } + + return "{{Partition(" + maxMembers + "," + wildcardList + ")}}." + destFromFilter; + } + @Override @NonNull public String toJson() { diff --git a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java index ec06a23..f683c6d 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/ElasticConsumerGroupTest.java @@ -16,6 +16,7 @@ import io.nats.NatsRunnerUtils; import io.nats.client.support.JsonParseException; import io.synadia.pcg.exceptions.ConsumerGroupException; +import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; @@ -37,16 +38,28 @@ class ElasticConsumerGroupTest { NatsRunnerUtils.setDefaultOutputLevel(Level.SEVERE); } + private static @NonNull List getPartitioningFilters() { + return Collections.singletonList(new PartitioningFilter()); + } + + private static @NonNull List getPartitioningFilters(String filter, int... partitioningWildcards) { + return Collections.singletonList(new PartitioningFilter(filter, partitioningWildcards)); + } + + private static @NonNull List getPartitioningFilters(String filter) { + return Collections.singletonList(new PartitioningFilter(filter)); + } + @Test void testConfigBasic() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 1000, 10000, + 4, getPartitioningFilters("foo.*", 1), 1000, 10000, Arrays.asList("m1", "m2"), new ArrayList<>() ); assertEquals(4, config.getMaxMembers()); - assertEquals("foo.*", config.getFilter()); - assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); + assertEquals("foo.*", config.getPartitioningFilters().get(0).getFilter()); + assertArrayEquals(new int[]{1}, config.getPartitioningFilters().get(0).getPartitioningWildcards()); assertEquals(1000, config.getMaxBufferedMessages()); assertEquals(10000, config.getMaxBufferedBytes()); assertEquals(2, config.getMembers().size()); @@ -56,7 +69,7 @@ void testConfigBasic() { @Test void testIsInMembership() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.*", 1), 0, 0, Arrays.asList("m1", "m2", "m3"), new ArrayList<>() ); @@ -74,7 +87,7 @@ void testIsInMembershipWithMappings() { ); ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.*", 1), 0, 0, new ArrayList<>(), mappings ); @@ -86,7 +99,7 @@ void testIsInMembershipWithMappings() { @Test void testValidationMaxMembersZero() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 0, "foo.*", new int[]{1}, 0, 0, + 0, getPartitioningFilters("foo.*", 1), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -97,7 +110,7 @@ void testValidationMaxMembersZero() { @Test void testValidationFilterNoWildcardNoGt() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.bar", new int[]{}, 0, 0, + 4, getPartitioningFilters("foo.bar"), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -108,7 +121,7 @@ void testValidationFilterNoWildcardNoGt() { @Test void testValidationFilterEndingWithGtIsValid() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.>", new int[]{}, 0, 0, + 4, getPartitioningFilters("foo.>"), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -118,7 +131,7 @@ void testValidationFilterEndingWithGtIsValid() { @Test void testValidationFilterNoWildcardWithWildcardsSpecified() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.bar", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.bar", 1), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -129,7 +142,7 @@ void testValidationFilterNoWildcardWithWildcardsSpecified() { @Test void testValidationPartitioningWildcardsEmptyIsValid() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{}, 0, 0, + 4, getPartitioningFilters("foo.*"), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -137,29 +150,29 @@ void testValidationPartitioningWildcardsEmptyIsValid() { } @Test - void testValidationNoFilterIsValid() { + void testValidationNoFilterIsNotValid() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, null, new int[]{}, 0, 0, + 4, getPartitioningFilters(null), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); - assertDoesNotThrow(config::validate); + assertThrows(ConsumerGroupException.class, config::validate); } @Test - void testValidationEmptyFilterIsValid() { + void testValidationEmptyFilterIsNotValid() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "", new int[]{}, 0, 0, + 4, getPartitioningFilters(""), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); - assertDoesNotThrow(config::validate); + assertThrows(ConsumerGroupException.class, config::validate); } @Test void testValidationPartitioningWildcardsTooMany() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1, 2}, 0, 0, // Only 1 wildcard in filter + 4, getPartitioningFilters("foo.*", 1, 2), 0, 0, // Only 1 wildcard in filter Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -170,7 +183,7 @@ void testValidationPartitioningWildcardsTooMany() { @Test void testValidationPartitioningWildcardsOutOfRange() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{2}, 0, 0, // Index 2 is out of range (only 1 wildcard) + 4, getPartitioningFilters("foo.*", 2), 0, 0, // Index 2 is out of range (only 1 wildcard) Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -181,7 +194,7 @@ void testValidationPartitioningWildcardsOutOfRange() { @Test void testValidationPartitioningWildcardsZeroIndex() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{0}, 0, 0, + 4, getPartitioningFilters("foo.*", 0), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -192,7 +205,7 @@ void testValidationPartitioningWildcardsZeroIndex() { @Test void testValidationPartitioningWildcardsDuplicate() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*.bar.*", new int[]{1, 1}, 0, 0, + 4, getPartitioningFilters("foo.*.bar.*", 1, 1), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -207,7 +220,7 @@ void testValidationBothMembersAndMappings() { ); ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.*", 1), 0, 0, Arrays.asList("m1", "m2"), mappings ); @@ -218,7 +231,7 @@ void testValidationBothMembersAndMappings() { @Test void testValidationSuccess() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.*", 1), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -233,7 +246,7 @@ void testValidationSuccessWithMappings() { ); ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.*", 1), 0, 0, new ArrayList<>(), mappings ); @@ -243,7 +256,7 @@ void testValidationSuccessWithMappings() { @Test void testValidationSuccessMultipleWildcards() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*.bar.*", new int[]{1, 2}, 0, 0, + 4, getPartitioningFilters("foo.*.bar.*", 1, 2), 0, 0, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -253,73 +266,73 @@ void testValidationSuccessMultipleWildcards() { @Test void testGetPartitioningTransformDestSingle() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.*", 1), 0, 0, new ArrayList<>(), new ArrayList<>() ); - String dest = config.getPartitioningTransformDest(); + String dest = config.getPartitioningFilters().get(0).getPartitioningTransformDest(4); assertEquals("{{Partition(4,1)}}.foo.{{Wildcard(1)}}", dest); } @Test void testGetPartitioningTransformDestMultiple() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 6, "foo.*.bar.*", new int[]{1, 2}, 0, 0, + 6, getPartitioningFilters("foo.*.bar.*", 1, 2), 0, 0, new ArrayList<>(), new ArrayList<>() ); - String dest = config.getPartitioningTransformDest(); + String dest = config.getPartitioningFilters().get(0).getPartitioningTransformDest(6); assertEquals("{{Partition(6,1,2)}}.foo.{{Wildcard(1)}}.bar.{{Wildcard(2)}}", dest); } @Test void testGetPartitioningTransformDestPartialWildcards() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 8, "a.*.b.*.c.*", new int[]{2}, 0, 0, + 8, getPartitioningFilters("a.*.b.*.c.*", 2), 0, 0, new ArrayList<>(), new ArrayList<>() ); - String dest = config.getPartitioningTransformDest(); + String dest = config.getPartitioningFilters().get(0).getPartitioningTransformDest(8); assertEquals("{{Partition(8,2)}}.a.{{Wildcard(1)}}.b.{{Wildcard(2)}}.c.{{Wildcard(3)}}", dest); } @Test void testGetPartitioningTransformDestNoWildcards() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{}, 0, 0, + 4, getPartitioningFilters("foo.*"), 0, 0, new ArrayList<>(), new ArrayList<>() ); - String dest = config.getPartitioningTransformDest(); + String dest = config.getPartitioningFilters().get(0).getPartitioningTransformDest(4); assertEquals("{{Partition(4)}}.foo.{{Wildcard(1)}}", dest); } @Test void testGetPartitioningTransformDestNoFilter() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, null, new int[]{}, 0, 0, + 4, getPartitioningFilters(null), 0, 0, new ArrayList<>(), new ArrayList<>() ); - String dest = config.getPartitioningTransformDest(); + String dest = config.getPartitioningFilters().get(0).getPartitioningTransformDest(4); assertEquals("{{Partition(4)}}.>", dest); } @Test void testGetPartitioningTransformDestEmptyFilter() { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "", new int[]{}, 0, 0, + 4, getPartitioningFilters(""), 0, 0, new ArrayList<>(), new ArrayList<>() ); - String dest = config.getPartitioningTransformDest(); + String dest = config.getPartitioningFilters().get(0).getPartitioningTransformDest(4); assertEquals("{{Partition(4)}}.>", dest); } @Test void testJsonSerializationWithMembers() throws JsonParseException { ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 1000, 10000, + 4, getPartitioningFilters("foo.*", 1), 1000, 10000, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -336,8 +349,8 @@ void testJsonSerializationWithMembers() throws JsonParseException { // Deserialize and verify ElasticConsumerGroupConfig deserialized = ElasticConsumerGroupConfig.instance(config.serialize()); assertEquals(config.getMaxMembers(), deserialized.getMaxMembers()); - assertEquals(config.getFilter(), deserialized.getFilter()); - assertArrayEquals(config.getPartitioningWildcards(), deserialized.getPartitioningWildcards()); + assertEquals(config.getPartitioningFilters().get(0).getFilter(), deserialized.getPartitioningFilters().get(0).getFilter()); + assertArrayEquals(config.getPartitioningFilters().get(0).getPartitioningWildcards(), deserialized.getPartitioningFilters().get(0).getPartitioningWildcards()); assertEquals(config.getMaxBufferedMessages(), deserialized.getMaxBufferedMessages()); assertEquals(config.getMaxBufferedBytes(), deserialized.getMaxBufferedBytes()); assertEquals(config.getMembers(), deserialized.getMembers()); @@ -351,7 +364,7 @@ void testJsonSerializationWithMappings() throws JsonParseException { ); ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 0, 0, + 4, getPartitioningFilters("foo.*", 1), 0, 0, new ArrayList<>(), mappings ); @@ -371,13 +384,13 @@ void testJsonSerializationWithMappings() throws JsonParseException { @Test void testJsonDeserializationFromGo() throws JsonParseException { // This JSON is in the format produced by the Go implementation - String goJson = "{\"max_members\":4,\"filter\":\"foo.*\",\"partitioning_wildcards\":[1],\"max_buffered_msg\":1000,\"max_buffered_bytes\":10000,\"members\":[\"m1\",\"m2\"]}"; + String goJson = "{\"max_members\":4,\"partitioning_filters\":[{\"filter\":\"foo.*\",\"partitioning_wildcards\":[1]}],\"max_buffered_msg\":1000,\"max_buffered_bytes\":10000,\"members\":[\"m1\",\"m2\"]}"; ElasticConsumerGroupConfig config = ElasticConsumerGroupConfig.instance(goJson.getBytes(StandardCharsets.UTF_8)); assertEquals(4, config.getMaxMembers()); - assertEquals("foo.*", config.getFilter()); - assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); + assertEquals("foo.*", config.getPartitioningFilters().get(0).getFilter()); + assertArrayEquals(new int[]{1}, config.getPartitioningFilters().get(0).getPartitioningWildcards()); assertEquals(1000, config.getMaxBufferedMessages()); assertEquals(10000, config.getMaxBufferedBytes()); assertEquals(2, config.getMembers().size()); @@ -388,13 +401,13 @@ void testJsonDeserializationFromGo() throws JsonParseException { @Test void testJsonDeserializationWithMappingsFromGo() throws JsonParseException { // This JSON is in the format produced by the Go implementation - String goJson = "{\"max_members\":4,\"filter\":\"bar.*\",\"partitioning_wildcards\":[1],\"member_mappings\":[{\"member\":\"alice\",\"partitions\":[0,1]},{\"member\":\"bob\",\"partitions\":[2,3]}]}"; + String goJson = "{\"max_members\":4,\"partitioning_filters\":[{\"filter\":\"bar.*\",\"partitioning_wildcards\":[1]}],\"member_mappings\":[{\"member\":\"alice\",\"partitions\":[0,1]},{\"member\":\"bob\",\"partitions\":[2,3]}]}"; ElasticConsumerGroupConfig config = ElasticConsumerGroupConfig.instance(goJson.getBytes(StandardCharsets.UTF_8)); assertEquals(4, config.getMaxMembers()); - assertEquals("bar.*", config.getFilter()); - assertArrayEquals(new int[]{1}, config.getPartitioningWildcards()); + assertEquals("bar.*", config.getPartitioningFilters().get(0).getFilter()); + assertArrayEquals(new int[]{1}, config.getPartitioningFilters().get(0).getPartitioningWildcards()); assertEquals(2, config.getMemberMappings().size()); assertEquals("alice", config.getMemberMappings().get(0).getMember()); assertArrayEquals(new int[]{0, 1}, config.getMemberMappings().get(0).getPartitions()); @@ -405,17 +418,17 @@ void testJsonDeserializationWithMappingsFromGo() throws JsonParseException { @Test void testEquals() { ElasticConsumerGroupConfig config1 = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 1000, 10000, + 4, getPartitioningFilters("foo.*", 1), 1000, 10000, Arrays.asList("m1", "m2"), new ArrayList<>() ); ElasticConsumerGroupConfig config2 = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 1000, 10000, + 4, getPartitioningFilters("foo.*", 1), 1000, 10000, Arrays.asList("m1", "m2"), new ArrayList<>() ); ElasticConsumerGroupConfig config3 = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 1000, 10000, + 4, getPartitioningFilters("foo.*", 1), 1000, 10000, Arrays.asList("m1", "m3"), new ArrayList<>() ); @@ -426,12 +439,12 @@ void testEquals() { @Test void testHashCode() { ElasticConsumerGroupConfig config1 = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 1000, 10000, + 4, getPartitioningFilters("foo.*", 1), 1000, 10000, Arrays.asList("m1", "m2"), new ArrayList<>() ); ElasticConsumerGroupConfig config2 = new ElasticConsumerGroupConfig( - 4, "foo.*", new int[]{1}, 1000, 10000, + 4, getPartitioningFilters("foo.*", 1), 1000, 10000, Arrays.asList("m1", "m2"), new ArrayList<>() ); @@ -451,28 +464,28 @@ void testRevision() { assertFalse(json.contains("revision")); } - @Test - void testGetPartitionFilters() { - ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( - 6, "foo.*", new int[]{1}, 0, 0, - Arrays.asList("m1", "m2", "m3"), new ArrayList<>() - ); - - List m1Filters = ElasticConsumerGroup.getPartitionFilters(config, "m1"); - List m2Filters = ElasticConsumerGroup.getPartitionFilters(config, "m2"); - List m3Filters = ElasticConsumerGroup.getPartitionFilters(config, "m3"); - - // Each member should get 2 partitions - assertEquals(2, m1Filters.size()); - assertEquals(2, m2Filters.size()); - assertEquals(2, m3Filters.size()); - - // Verify distribution - assertTrue(m1Filters.contains("0.>")); - assertTrue(m1Filters.contains("1.>")); - assertTrue(m2Filters.contains("2.>")); - assertTrue(m2Filters.contains("3.>")); - assertTrue(m3Filters.contains("4.>")); - assertTrue(m3Filters.contains("5.>")); - } +// @Test +// void testGetPartitionFilters() { +// ElasticConsumerGroupConfig config = new ElasticConsumerGroupConfig( +// 6, getPartitioningFilters("foo.*", 1), 0, 0, +// Arrays.asList("m1", "m2", "m3"), new ArrayList<>() +// ); +// +// List m1Filters = ElasticConsumerGroup.getPartitionFilters(config, "m1"); +// List m2Filters = ElasticConsumerGroup.getPartitionFilters(config, "m2"); +// List m3Filters = ElasticConsumerGroup.getPartitionFilters(config, "m3"); +// +// // Each member should get 2 partitions +// assertEquals(2, m1Filters.size()); +// assertEquals(2, m2Filters.size()); +// assertEquals(2, m3Filters.size()); +// +// // Verify distribution +// assertTrue(m1Filters.contains("0.>")); +// assertTrue(m1Filters.contains("1.>")); +// assertTrue(m2Filters.contains("2.>")); +// assertTrue(m2Filters.contains("3.>")); +// assertTrue(m3Filters.contains("4.>")); +// assertTrue(m3Filters.contains("5.>")); +// } } diff --git a/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java index 13b2586..30b5134 100644 --- a/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java +++ b/pcgroups/src/test/java/io/synadia/pcg/IntegrationTest.java @@ -158,9 +158,10 @@ void testElastic() throws Exception { .ackWait(Duration.ofSeconds(1)) .build(); + PartitioningFilter pf = new PartitioningFilter("bar.*", new int[]{1}); // Create elastic consumer group - ElasticConsumerGroup.create(nc, streamName, cgName, 2, "bar.*", - new int[]{1}, -1, -1); + ElasticConsumerGroup.create(nc, streamName, cgName, 2, + Collections.singletonList(pf), -1, -1); // Start consuming on both members ConsumerGroupConsumeContext cc1 = ElasticConsumerGroup.consume(nc, streamName, cgName, "m1", msg -> { @@ -250,8 +251,7 @@ void testElastic() throws Exception { AtomicInteger c4 = new AtomicInteger(0); // Create elastic consumer group with no filter (null) and empty wildcards - ElasticConsumerGroup.create(nc, streamName, cgName2, 2, null, - new int[]{}, -1, -1); + ElasticConsumerGroup.create(nc, streamName, cgName2, 2, null, -1, -1); // Start consuming on both members ConsumerGroupConsumeContext cc3 = ElasticConsumerGroup.consume(nc, streamName, cgName2, "m1", msg -> { From fb154ce459738f77b213609db1573f313c00d295 Mon Sep 17 00:00:00 2001 From: scottf Date: Sat, 14 Mar 2026 08:40:20 -0400 Subject: [PATCH 128/135] add release script for pcgcli --- .github/workflows/pcgcli-release.yml | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/pcgcli-release.yml diff --git a/.github/workflows/pcgcli-release.yml b/.github/workflows/pcgcli-release.yml new file mode 100644 index 0000000..ccbe248 --- /dev/null +++ b/.github/workflows/pcgcli-release.yml @@ -0,0 +1,36 @@ +name: Partitioned Consumer Groups CLI Release + +on: + push: + tags: [ 'pcgcli/*' ] + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + defaults: + run: + working-directory: ./pcgroups + steps: + - name: Set up JDK + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + + - name: Check out code + uses: actions/checkout@v4 + + - name: Build distribution + run: chmod +x gradlew && ./gradlew :pcgroups-cli:clean :pcgroups-cli:dist + + - name: Upload release assets + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + CLI_BUILD="../pcgroups-cli/build" + gh release upload "${{ github.ref_name }}" \ + "$CLI_BUILD/cg.jar" \ + "$CLI_BUILD/cg.tar" \ + --clobber From fdd94bb91cca1aa667bdcf9b72ef8d1ba3e7cc69 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 12 May 2026 17:23:37 -0400 Subject: [PATCH 129/135] Cancel Schedule --- .../main/java/io/synadia/retrier/Retrier.java | 2 +- schedule-message/build.gradle | 2 +- .../io/synadia/examples/ScheduleBasics.java | 27 +- .../examples/ScheduleBasicsAlternate.java | 97 ++++++ .../synadia/examples/ScheduleFromSource.java | 12 +- .../io/synadia/sm/ScheduleManagement.java | 304 ++++++++++++++++++ .../synadia/sm/ScheduledMessageBuilder.java | 103 +++++- .../io/synadia/sm/ScheduledStreamUtil.java | 34 -- .../synadia/sm/ScheduleManagementTests.java | 253 +++++++++++++++ 9 files changed, 775 insertions(+), 59 deletions(-) create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java create mode 100644 schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java delete mode 100644 schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java create mode 100644 schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java diff --git a/retrier/src/main/java/io/synadia/retrier/Retrier.java b/retrier/src/main/java/io/synadia/retrier/Retrier.java index 2fdc49d..dc3249a 100644 --- a/retrier/src/main/java/io/synadia/retrier/Retrier.java +++ b/retrier/src/main/java/io/synadia/retrier/Retrier.java @@ -33,7 +33,7 @@ public static T execute(RetryConfig config, RetryAction action) throws Ex * or the observer declines to retry. */ public static T execute(RetryConfig config, RetryAction action, RetryObserver observer) throws Exception { - long[] backoffPolicy = config.getBackoffPolicy();; + long[] backoffPolicy = config.getBackoffPolicy(); int plen = backoffPolicy.length; int retries = 0; long deadlineExpiresAt = System.currentTimeMillis() + config.getDeadline(); diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index 2a61b39..406e1f3 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -40,7 +40,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.25.3-SNAPSHOT' + implementation 'io.nats:jnats:2.25.3' implementation 'org.jspecify:jspecify:1.0.0' implementation 'io.synadia:counters:0.2.2' diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java index 631a00b..ca7160e 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java @@ -7,8 +7,8 @@ import io.nats.client.api.StorageType; import io.nats.client.api.StreamInfo; import io.nats.client.support.DateTimeUtils; +import io.synadia.sm.ScheduleManagement; import io.synadia.sm.ScheduledMessageBuilder; -import io.synadia.sm.ScheduledStreamUtil; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -34,14 +34,14 @@ public static void main(String[] args) { .build(); try (Connection connection = Nats.connect(options)) { - JetStreamManagement jsm = connection.jetStreamManagement();; + JetStreamManagement jsm = connection.jetStreamManagement(); JetStream js = connection.jetStream(); // delete the stream in case it existed, just for a fresh example try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} // Use the utility to properly create a schedulable stream - StreamInfo si = ScheduledStreamUtil.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + StreamInfo si = ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); report("Created stream", si.getConfiguration()); CountDownLatch latch = new CountDownLatch(4); @@ -60,32 +60,29 @@ public static void main(String[] args) { latch.countDown(); }, false); - Message m = new ScheduledMessageBuilder() + report("SCHEDULE-NOW (publishing)"); + new ScheduledMessageBuilder() .scheduleSubject(SCHEDULE_PREFIX + "now") .targetSubject(TARGET_PREFIX + "now") .scheduleImmediate() .data("Schedule-Now") - .build(); - report("SCHEDULE-NOW (sending)", m); - js.publish(m); + .scheduleMessage(js); - m = new ScheduledMessageBuilder() + report("SCHEDULE-AT (publishing)"); + new ScheduledMessageBuilder() .scheduleSubject(SCHEDULE_PREFIX + "at") .targetSubject(TARGET_PREFIX + "at") .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(5)) .data("Scheduled-At") - .build(); - report("SCHEDULE-AT (sending)", m); - js.publish(m); + .scheduleMessage(js); - m = new ScheduledMessageBuilder() + report("SCHEDULE-EVERY (publishing)"); + new ScheduledMessageBuilder() .scheduleSubject(SCHEDULE_PREFIX + "at") .targetSubject(TARGET_PREFIX + "at") .scheduleEvery(1, TimeUnit.SECONDS) .data("Every Second") - .build(); - report("SCHEDULE-EVERY (sending)", m); - js.publish(m); + .scheduleMessage(js); latch.await(); } diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java new file mode 100644 index 0000000..2cb4e35 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java @@ -0,0 +1,97 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamInfo; +import io.nats.client.support.DateTimeUtils; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.synadia.examples.ScheduleUtils.report; + +public class ScheduleBasicsAlternate { + public static final String STREAM = "schedules-enabled"; + + public static final String SCHEDULE_PREFIX = "schedule."; + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + // Use the utility to properly create a schedulable stream + StreamInfo si = ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + report("Created stream", si.getConfiguration()); + + CountDownLatch latch = new CountDownLatch(4); + Dispatcher d = connection.createDispatcher(); + + // subscribe to the subject that receives the schedule message + js.subscribe(SCHEDULES, d, m -> { + report("SCHEDULED (received)", m); + m.ack(); + }, false); + + // subscribe to the target subject + js.subscribe(TARGETS, d, m -> { + report("TARGETED (received)", m); + m.ack(); + latch.countDown(); + }, false); + + Message m = new ScheduledMessageBuilder() + .scheduleSubject(SCHEDULE_PREFIX + "now") + .targetSubject(TARGET_PREFIX + "now") + .scheduleImmediate() + .data("Schedule-Now") + .build(); + report("SCHEDULE-NOW (publishing)", m); + js.publish(m); + + m = new ScheduledMessageBuilder() + .scheduleSubject(SCHEDULE_PREFIX + "at") + .targetSubject(TARGET_PREFIX + "at") + .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(5)) + .data("Scheduled-At") + .build(); + report("SCHEDULE-AT (publishing)", m); + js.publish(m); + + m = new ScheduledMessageBuilder() + .scheduleSubject(SCHEDULE_PREFIX + "at") + .targetSubject(TARGET_PREFIX + "at") + .scheduleEvery(1, TimeUnit.SECONDS) + .data("Every Second") + .build(); + report("SCHEDULE-EVERY (publishing)", m); + js.publish(m); + + latch.await(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java index 0627381..4062012 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java @@ -8,8 +8,8 @@ import io.nats.client.api.StreamInfo; import io.nats.client.impl.Headers; import io.nats.client.impl.NatsMessage; +import io.synadia.sm.ScheduleManagement; import io.synadia.sm.ScheduledMessageBuilder; -import io.synadia.sm.ScheduledStreamUtil; import java.time.Duration; import java.util.concurrent.CountDownLatch; @@ -40,7 +40,7 @@ public static void main(String[] args) { try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} // Use the utility to properly create a schedulable stream - StreamInfo si = ScheduledStreamUtil.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + StreamInfo si = ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); report("Created stream", si.getConfiguration()); CountDownLatch latch1 = new CountDownLatch(1); @@ -72,7 +72,7 @@ public static void main(String[] args) { Headers sourceHeaders = new Headers(); sourceHeaders.put("foo1", "bar1"); Message sourceMessage = new NatsMessage(SOURCE, null, sourceHeaders, sourceData.getBytes()); - report("SOURCE 1 (sending)", sourceMessage); + report("SOURCE 1 (publishing)", sourceMessage); js.publish(sourceMessage); connection.flush(Duration.ofSeconds(1)); @@ -82,7 +82,7 @@ public static void main(String[] args) { .scheduleImmediate() .sources(SOURCE) .build(); - report("SCHEDULE 1 (sending)", scheduleMessage); + report("SCHEDULE 1 (publishing)", scheduleMessage); js.publish(scheduleMessage); latch1.await(); @@ -91,11 +91,11 @@ public static void main(String[] args) { sourceHeaders = new Headers(); sourceHeaders.put("foo2", "bar2"); sourceMessage = new NatsMessage(SOURCE, null, sourceHeaders, sourceData.getBytes()); - report("SOURCE 2 (sending)", sourceMessage); + report("SOURCE 2 (publishing)", sourceMessage); js.publish(sourceMessage); connection.flush(Duration.ofSeconds(1)); - report("SCHEDULE 2 (sending)", scheduleMessage); + report("SCHEDULE 2 (publishing)", scheduleMessage); js.publish(scheduleMessage); latch2.await(); diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java new file mode 100644 index 0000000..08914ec --- /dev/null +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java @@ -0,0 +1,304 @@ +package io.synadia.sm; + +import io.nats.client.*; +import io.nats.client.api.*; +import io.nats.client.impl.Headers; +import io.nats.client.impl.NatsMessage; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.io.IOException; +import java.util.List; + +import static io.nats.client.support.NatsJetStreamConstants.*; +import static io.nats.client.support.Validator.notPrintableOrHasWildGt; + +/** + * Helper utilities for stopping NATS message schedules early, per + * ADR-51 + * (section Ending/stopping schedules early), plus a couple of convenience helpers for + * creating schedule-capable streams. + *

    + * The class exposes two families of operations: + *

      + *
    • Basic stop — remove the schedule message from its stream so it can no + * longer fire. {@link #cancelSchedule(JetStreamManagement, String, long)} deletes by + * stream sequence; the subject-based overloads look the sequence up first.
    • + *
    • Atomic publish-and-stop — publish a message to a different subject and stop + * the schedule as a single atomic step, optionally guarded by an existence check on + * the schedule message. See + * {@link #publishAndCancelSchedule(JetStreamManagement, String, String, byte[], Headers, boolean)} + * and {@link #publishAndCancelSchedule(JetStreamManagement, String, long, String, byte[], Headers)}.
    • + *
    + * Per the ADR the publish subject of the atomic variants must not equal the schedule + * subject; the server rejects such publishes with error code {@code 10212}. + *

    + * All methods are static; the class is {@code abstract} purely to prevent instantiation. + */ +@NullMarked +public abstract class ScheduleManagement { + + /** + * Outcome of a {@code cancelSchedule(...)} call. + */ + public enum Result { + /** The schedule message was found and successfully deleted. */ + SUCCESS, + /** The server-side delete returned {@code false}. */ + FAILURE, + /** No schedule message was found for the given subject / sequence. */ + NOT_FOUND + } + + /** + * Add a new stream with message scheduling enabled. + * Both {@code AllowMsgSchedules} and {@code AllowMsgTTL} are set on the stream — the + * latter is required for the {@code Nats-Schedule-TTL} header to take effect on + * messages produced by schedules. + * + * @param jsm the JetStream management context + * @param streamName the stream name + * @param storageType the storage type ({@code File} or {@code Memory}) + * @param subjects the subjects the stream will accept; must cover both the + * schedule subjects and any target subjects schedules publish to + * @return the created {@link StreamInfo} + * @throws JetStreamApiException if the server returned an error + * @throws IOException if the request could not be sent + */ + public static StreamInfo createSchedulableStream(JetStreamManagement jsm, String streamName, StorageType storageType, String... subjects) throws JetStreamApiException, IOException { + StreamConfiguration sc = StreamConfiguration.builder() + .name(streamName) + .storageType(storageType) + .subjects(subjects) + .allowMessageSchedules() + .allowMessageTtl() + .build(); + return jsm.addStream(sc); + } + + /** + * Add a new stream with message scheduling enabled, derived from an existing + * {@link StreamConfiguration}. The supplied configuration is copied and + * {@code AllowMsgSchedules} / {@code AllowMsgTTL} are turned on; all other settings + * are preserved. + * + * @param jsm the JetStream management context + * @param startingStreamConfig the base configuration to copy from + * @return the created {@link StreamInfo} + * @throws JetStreamApiException if the server returned an error + * @throws IOException if the request could not be sent + */ + public static StreamInfo createSchedulableStream(JetStreamManagement jsm, StreamConfiguration startingStreamConfig) throws JetStreamApiException, IOException { + StreamConfiguration sc = StreamConfiguration.builder(startingStreamConfig) + .allowMessageSchedules() + .allowMessageTtl() + .build(); + return jsm.addStream(sc); + } + + /** + * Stop a schedule by deleting its message at a specific stream sequence (ADR-51 + * mechanism: delete by stream sequence). The schedule stops firing as soon as + * its message is removed. + * + * @param jsm the JetStream management context + * @param stream the stream that holds the schedule message + * @param scheduleStreamSequence the stream sequence of the schedule message + * @return {@link Result#SUCCESS} on a successful delete, {@link Result#FAILURE} if + * the server reported the delete as unsuccessful, or {@link Result#NOT_FOUND} if + * no message exists at that sequence (server error {@code 10043}). Any other + * server error is rethrown. + * @throws JetStreamApiException if the server returned an error other than + * "message not found" + * @throws IOException if the request could not be sent + */ + public static Result cancelSchedule(JetStreamManagement jsm, String stream, long scheduleStreamSequence) throws JetStreamApiException, IOException { + try { + return jsm.deleteMessage(stream, scheduleStreamSequence) ? Result.SUCCESS : Result.FAILURE; + } + catch (JetStreamApiException e) { + if (e.getApiErrorCode() == 10043) { + return Result.NOT_FOUND; + } + throw e; + } + } + + /** + * Convenience overload that locates the stream that owns the schedule subject before + * delegating to {@link #cancelSchedule(JetStreamManagement, String, String)}. + *

    + * The lookup is strict: it fails if zero streams match the subject, and refuses to + * pick one when more than one stream matches. Pass the stream name explicitly to the + * three-argument overload if you need to disambiguate. + * + * @param jsm the JetStream management context + * @param scheduleSubject the schedule subject + * @return see {@link #cancelSchedule(JetStreamManagement, String, long)} + * @throws IllegalStateException if no stream — or more than one — covers the subject + * @throws JetStreamApiException if the server returned an error + * @throws IOException if the request could not be sent + */ + public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubject) throws JetStreamApiException, IOException { + return cancelSchedule(jsm, scheduleSubject, findStream(jsm, scheduleSubject)); + } + + /** + * Stop a schedule identified by its subject in the given stream. Looks up the last + * message on the subject, verifies it is a schedule message (has the + * {@code Nats-Schedule} header), and deletes it by its stream sequence. + * + * @param jsm the JetStream management context + * @param scheduleSubject the exact schedule subject (wildcards are not supported) + * @param scheduleStream the name of the stream that holds the schedule message + * @return {@link Result#NOT_FOUND} if no schedule message exists on the subject; + * otherwise the result of the underlying sequence-based delete + * @throws JetStreamApiException if the server returned an error + * @throws IOException if the request could not be sent + */ + public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubject, String scheduleStream) throws JetStreamApiException, IOException { + if (notPrintableOrHasWildGt(scheduleSubject)) { + // this is a wildcard subject so we must use purge + PurgeResponse response = jsm.purgeStream(scheduleStream, PurgeOptions.builder().subject(scheduleSubject).build()); + if (response.isSuccess()) { + return response.getPurged() > 0 ? Result.SUCCESS : Result.NOT_FOUND; + } + return Result.FAILURE; + } + + long seq = getScheduleSequence(jsm, scheduleStream, scheduleSubject); + if (seq == -1) { + return Result.NOT_FOUND; + } + return cancelSchedule(jsm, scheduleStream, seq); + } + + /** + * Atomically publish a message and stop the named schedule, per ADR-51's + * atomic stop mechanism. The published message carries: + *

      + *
    • {@code Nats-Scheduler}: {@code scheduleSubject}
    • + *
    • {@code Nats-Schedule-Next}: {@code purge}
    • + *
    + * The {@code targetSubject} must not equal {@code scheduleSubject} — the server + * rejects such publishes with error code {@code 10212}. + * + * @param jsm the JetStream management context (its + * {@code jetStream()} context is used to publish) + * @param scheduleSubject the schedule subject to stop + * @param targetSubject the subject to publish to; this may be the + * original schedule's target subject (to publish + * early) or any other subject + * @param data the message body + * @param publishOnlyIfScheduleExists when {@code true}, the publish is sent with an + * expected-last-subject-sequence guard so it + * only succeeds if the schedule message is still + * present; when {@code false}, the publish is + * sent unconditionally + * @return the {@link PublishAck} from the server, or {@code null} when + * {@code publishOnlyIfScheduleExists} is {@code true} and no schedule for the + * subject could be located + * @throws JetStreamApiException if the server returned an error + * @throws IOException if the request could not be sent + */ + public static @Nullable PublishAck publishAndCancelSchedule(JetStreamManagement jsm, String scheduleSubject, String targetSubject, + byte @Nullable[] data, @Nullable Headers userHeaders, boolean publishOnlyIfScheduleExists) throws JetStreamApiException, IOException { + if (publishOnlyIfScheduleExists) { + String streamName = findStreamLenient(jsm, scheduleSubject); + if (streamName != null) { + long seq = getScheduleSequence(jsm, streamName, scheduleSubject); + if (seq != -1) { + return publishAndCancelSchedule(jsm, scheduleSubject, seq, targetSubject, data, userHeaders); + } + } + return null; + } + + Headers h = makeHeaders(scheduleSubject, userHeaders); + return jsm.jetStream().publish(targetSubject, h, data); + } + + /** + * Atomic publish-and-stop guarded by an explicit existence check. Same headers as + * the simpler overload, but additionally sets: + *
      + *
    • {@code Nats-Expected-Last-Subject-Sequence}: {@code scheduleStreamSequence}
    • + *
    • {@code Nats-Expected-Last-Subject-Sequence-Subject}: {@code scheduleSubject}
    • + *
    + * The publish — and therefore the stop — only succeeds if the schedule message is + * still present at the given sequence on its subject. Useful for stopping a schedule + * and publishing in one atomic step without risk of duplicating the message if the + * schedule fires concurrently. + * + * @param jsm the JetStream management context + * @param scheduleSubject the schedule subject to stop + * @param scheduleStreamSequence the expected stream sequence of the schedule message + * on {@code scheduleSubject} + * @param targetSubject the subject to publish to (must differ from + * {@code scheduleSubject}) + * @param data the message body + * @return the {@link PublishAck} from the server + * @throws JetStreamApiException if the precondition fails or the server returned + * another error + * @throws IOException if the request could not be sent + */ + public static PublishAck publishAndCancelSchedule(JetStreamManagement jsm, String scheduleSubject, long scheduleStreamSequence, String targetSubject, + byte @Nullable[] data, @Nullable Headers userHeaders) throws JetStreamApiException, IOException { + Headers h = makeHeaders(scheduleSubject, userHeaders); + PublishOptions opts = PublishOptions.builder() + .expectedLastSubjectSequenceSubject(scheduleSubject) + .expectedLastSubjectSequence(scheduleStreamSequence) + .build(); + Message m = new NatsMessage(targetSubject, null, h, data); + return jsm.jetStream().publish(m , opts); + } + + private static Headers makeHeaders(String scheduleSubject, @Nullable Headers userHeaders) { + Headers h = new Headers(); + h.put(NATS_SCHEDULE_NEXT_HDR, "purge"); + h.put(NATS_SCHEDULER_HDR, scheduleSubject); + if (userHeaders != null) { + for (String key : userHeaders.keySet()) { + h.put(key, userHeaders.get(key)); + } + } + return h; + } + + private static @Nullable String findStreamLenient(JetStreamManagement jsm, String scheduleSubject) throws JetStreamApiException, IOException { + List streams = jsm.getStreamNames(scheduleSubject); + if (streams == null || streams.size() != 1) { + return null; + } + return streams.get(0); + } + + private static String findStream(JetStreamManagement jsm, String scheduleSubject) throws JetStreamApiException, IOException { + List streams = jsm.getStreamNames(scheduleSubject); + if (streams == null || streams.isEmpty()) { + throw new IllegalStateException("No stream found for subject [" + scheduleSubject + "]"); + } + if (streams.size() != 1) { + throw new IllegalStateException("Subject matches more than 1 stream [" + scheduleSubject + "]"); + } + return streams.get(0); + } + + private static long getScheduleSequence(JetStreamManagement jsm, String streamName, String scheduleSubject) throws IOException, JetStreamApiException { + try { + MessageInfo mi = jsm.getLastMessage(streamName, scheduleSubject); + if (mi != null) { + Headers headers = mi.getHeaders(); + if (headers != null && headers.containsKey(NATS_SCHEDULE_HDR)) { + return mi.getSeq(); + } + } + } + catch (JetStreamApiException e) { + if (e.getApiErrorCode() != 10037) { + throw e; + } + } + return -1; + } +} diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index 4725560..d5d56b2 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -1,10 +1,13 @@ -// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. // See LICENSE and NOTICE file for details. package io.synadia.sm; +import io.nats.client.JetStream; +import io.nats.client.JetStreamApiException; import io.nats.client.Message; import io.nats.client.MessageTtl; +import io.nats.client.api.PublishAck; import io.nats.client.impl.Headers; import io.nats.client.impl.NatsMessage; import io.nats.client.support.DateTimeUtils; @@ -12,6 +15,7 @@ import io.nats.client.support.Validator; import org.jspecify.annotations.NonNull; +import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -22,11 +26,29 @@ import java.util.concurrent.TimeUnit; /** - * Class to make a message that can be published to a stream that allows message scheduling + * Builder for constructing a NATS JetStream {@link Message} that carries a schedule per ADR-51. + *

    + * The resulting message is published to a schedule subject on a stream that supports message + * scheduling. The message itself does not get delivered to subscribers directly; instead the + * server interprets the {@code Nats-Schedule-*} headers and produces published messages on the + * configured target subject according to the schedule. + *

    + * Three scheduling modes are supported: + *

      + *
    • {@code @at} - a one-shot schedule at a specific point in time + * (see {@link #scheduleAt}, {@link #scheduleIn}, {@link #scheduleImmediate}).
    • + *
    • {@code @every} - a fixed interval, with a minimum supported interval of 1 second + * (see {@link #scheduleEvery}).
    • + *
    • Cron / predefined - a standard cron expression or one of the predefined entries + * such as {@code @hourly}, {@code @daily}, etc. + * (see {@link #scheduleCron}, {@link #schedule(PredefinedSchedules)}).
    • + *
    */ public class ScheduledMessageBuilder { + /** Number of nanoseconds in one second. */ public static final long NANOS_PER_SECOND = 1_000_000_000L; + private String scheduleString; private String timezone; private String scheduleSubject; @@ -34,6 +56,7 @@ public class ScheduledMessageBuilder { private Headers headers; private byte[] data; private MessageTtl messageTtl; + private boolean rollup; private final List sources = new ArrayList<>(); public ScheduledMessageBuilder() {} @@ -105,6 +128,7 @@ public ScheduledMessageBuilder headers(Headers headers) { /** * Copy the subject, data and headers from an existing message * @param message the message + * @return the builder */ public ScheduledMessageBuilder copy(Message message) { scheduleSubject(message.getSubject()); @@ -228,11 +252,27 @@ public ScheduledMessageBuilder scheduleCron(String cron, String timezone) { return this; } + /** + * Set the {@code Nats-Schedule-TTL} header. This TTL is applied to each message + * the schedule publishes to the target subject (when the stream supports per-message TTLs); + * it is not a TTL on the schedule itself. + * @param messageTtl the per-published-message TTL + * @return the builder + */ public ScheduledMessageBuilder messageTtl(MessageTtl messageTtl) { this.messageTtl = messageTtl; return this; } + /** + * Set the {@code Nats-Schedule-Source} header. When set, the schedule reads the last + * message on the source subject and publishes it to the target subject; if no message + * exists on the source subject, the schedule's own body and headers are used as a + * fallback. Wildcards are not supported. Per ADR-51 conventions the header supports + * a list of source subjects. + * @param sources the source subjects + * @return the builder + */ public ScheduledMessageBuilder sources(List sources) { this.sources.clear(); if (sources != null) { @@ -241,6 +281,15 @@ public ScheduledMessageBuilder sources(List sources) { return this; } + /** + * Set the {@code Nats-Schedule-Source} header. When set, the schedule reads the last + * message on the source subject and publishes it to the target subject; if no message + * exists on the source subject, the schedule's own body and headers are used as a + * fallback. Wildcards are not supported. Per ADR-51 conventions the header supports + * a list of source subjects. + * @param sources the source subjects + * @return the builder + */ public ScheduledMessageBuilder sources(String... sources) { this.sources.clear(); if (sources != null) { @@ -249,6 +298,40 @@ public ScheduledMessageBuilder sources(String... sources) { return this; } + /** + * Set the {@code Nats-Schedule-Rollup} header to {@code sub}, which is the only + * valid value per ADR-51. This causes published messages to roll up the target subject. + * @return the builder + */ + public ScheduledMessageBuilder rollup() { + rollup = true; + return this; + } + + /** + * Build the scheduled message and publish it to JetStream. + * @param js the JetStream context used to publish + * @return the sequence number of the stored schedule message from the {@link PublishAck} + * @throws JetStreamApiException if the server returns an error + * @throws IOException if there is a communication problem with the server + */ + public long scheduleMessage(JetStream js) throws JetStreamApiException, IOException { + return js.publish(build()).getSeqno(); + } + + /** + * Build the fully constructed message ready to be published to the schedule subject. + *

    + * Validates that {@code scheduleSubject} and {@code targetSubject} are both supplied + * and printable without {@code *} or {@code >} wildcards, and that a schedule string + * has been set via one of the {@code scheduleXxx}/{@code schedule} methods. + *

    + * Sets the following headers as applicable: + * {@code Nats-Schedule}, {@code Nats-Schedule-Target}, + * {@code Nats-Schedule-TTL}, {@code Nats-Schedule-Time-Zone}, + * {@code Nats-Schedule-Source}, {@code Nats-Schedule-Rollup}. + * @return the constructed {@link Message} + */ public Message build() { Validator.required(scheduleSubject, "Publish Subject is required."); Validator.required(targetSubject, "Target Subject is required."); @@ -274,6 +357,9 @@ public Message build() { if (sources.size() > 0) { headers.put(NatsJetStreamConstants.NATS_SCHEDULE_SOURCE_HDR, sources); } + if (rollup) { + headers.put(NatsJetStreamConstants.NATS_SCHEDULE_ROLLUP_HDR, "sub"); + } return NatsMessage.builder() .subject(scheduleSubject) @@ -282,6 +368,12 @@ public Message build() { .build(); } + /** + * Format a {@link Duration} as a Go {@code time.ParseDuration()} string + * (for example {@code "1h30m5s"}). Used to render {@code @every} interval values. + * @param duration the duration to format + * @return the Go-formatted duration string + */ public static String toGoDuration(Duration duration) { long left = duration.toNanos(); long nanos = left % 1_000_000L; @@ -303,6 +395,13 @@ public static String toGoDuration(Duration duration) { return sb.toString(); } + /** + * Validate that a Go {@code time.ParseDuration()} formatted string represents a + * duration of at least one second, matching ADR-51's minimum supported interval + * rule for {@code @every} schedules. + * @param s the Go-formatted duration string + * @return {@code true} if the string parses successfully and is at least 1 second + */ public static boolean isAtLeastOneSecond(@NonNull String s) { long totalNanos = 0; int i = 0; diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java deleted file mode 100644 index 26147c9..0000000 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledStreamUtil.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.synadia.sm; - -import io.nats.client.JetStreamApiException; -import io.nats.client.JetStreamManagement; -import io.nats.client.api.StorageType; -import io.nats.client.api.StreamConfiguration; -import io.nats.client.api.StreamInfo; - -import java.io.IOException; - -/** - * Class to make setting a per message ttl easier. - */ -public abstract class ScheduledStreamUtil { - - public static StreamInfo createSchedulableStream(JetStreamManagement jsm, String streamName, StorageType storageType, String... subjects) throws JetStreamApiException, IOException { - StreamConfiguration sc = StreamConfiguration.builder() - .name(streamName) - .storageType(storageType) - .subjects(subjects) - .allowMessageSchedules() - .allowMessageTtl() - .build(); - return jsm.addStream(sc); - } - - public static StreamInfo createSchedulableStream(JetStreamManagement jsm, StreamConfiguration startingStreamConfig) throws JetStreamApiException, IOException { - StreamConfiguration sc = StreamConfiguration.builder(startingStreamConfig) - .allowMessageSchedules() - .allowMessageTtl() - .build(); - return jsm.addStream(sc); - } -} diff --git a/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java b/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java new file mode 100644 index 0000000..f9d554b --- /dev/null +++ b/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java @@ -0,0 +1,253 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.sm; + +import io.nats.NatsRunnerUtils; +import io.nats.NatsServerRunner; +import io.nats.client.*; +import io.nats.client.api.MessageInfo; +import io.nats.client.api.PublishAck; +import io.nats.client.api.StorageType; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.logging.Level; + +import static io.nats.client.support.NatsJetStreamConstants.NATS_SCHEDULE_HDR; +import static org.junit.jupiter.api.Assertions.*; + +public class ScheduleManagementTests { + + static NatsServerRunner runner; + static Connection nc; + static JetStreamManagement jsm; + static JetStream js; + + @BeforeAll + public static void beforeAll() throws Exception { + NatsRunnerUtils.setDefaultOutputLevel(Level.WARNING); + runner = new NatsServerRunner(false, true); + Options options = Options.builder() + .server(runner.getNatsLocalhostUri()) + .errorListener(new ErrorListener() {}) + .build(); + nc = Nats.connect(options); + jsm = nc.jetStreamManagement(); + js = nc.jetStream(); + } + + @AfterAll + public static void afterAll() throws Exception { + if (nc != null) nc.close(); + if (runner != null) runner.close(); + } + + // -- helpers --------------------------------------------------------------- + + /** Holder for a freshly created stream and the schedule / target subject prefixes scoped to it. */ + private static class Fixture { + final String stream; + final String schedPrefix; // e.g. "sched." + final String tgtPrefix; // e.g. "tgt." + Fixture(String stream, String schedPrefix, String tgtPrefix) { + this.stream = stream; + this.schedPrefix = schedPrefix; + this.tgtPrefix = tgtPrefix; + } + String sched(String leaf) { return schedPrefix + "." + leaf; } + String tgt(String leaf) { return tgtPrefix + "." + leaf; } + } + + /** Create a schedulable stream with unique schedule and target subject patterns. */ + private static Fixture newFixture() throws Exception { + String id = NUID.nextGlobalSequence(); + String stream = "stream_" + id; + String schedPrefix = "sched_" + id; + String tgtPrefix = "tgt_" + id; + ScheduleManagement.createSchedulableStream( + jsm, stream, StorageType.Memory, + schedPrefix + ".>", + tgtPrefix + ".>"); + return new Fixture(stream, schedPrefix, tgtPrefix); + } + + /** + * Schedule a single delayed message far enough in the future that it cannot fire + * during the test. Returns the stream sequence the schedule message landed at. + */ + private static long scheduleInTheFuture(String schedSubject, String targetSubject, String data) throws Exception { + return new ScheduledMessageBuilder() + .scheduleSubject(schedSubject) + .targetSubject(targetSubject) + .scheduleIn(Duration.ofHours(1)) + .data(data) + .scheduleMessage(js); + } + + /** True iff a schedule message still exists on the subject (carries the Nats-Schedule header). */ + private static boolean scheduleExists(String streamName, String schedSubject) throws Exception { + try { + MessageInfo mi = jsm.getLastMessage(streamName, schedSubject); + return mi != null + && mi.getHeaders() != null + && mi.getHeaders().containsKey(NATS_SCHEDULE_HDR); + } + catch (JetStreamApiException e) { + if (e.getApiErrorCode() == 10037) { + return false; + } + throw e; + } + } + + // -- cancelSchedule(jsm, stream, scheduleStreamSequence) ------------------- + + @Test + public void testCancelBySequence() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("a"); + long seq = scheduleInTheFuture(schedSubject, f.tgt("a"), "body"); + + assertTrue(scheduleExists(f.stream, schedSubject)); + assertEquals(ScheduleManagement.Result.SUCCESS, ScheduleManagement.cancelSchedule(jsm, f.stream, seq)); + assertFalse(scheduleExists(f.stream, schedSubject)); + + assertEquals(ScheduleManagement.Result.NOT_FOUND, + ScheduleManagement.cancelSchedule(jsm, f.stream, 99_999L)); + } + + // -- cancelSchedule(jsm, scheduleSubject) ---------------------------------- + + @Test + public void testCancelBySubject_success() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("b"); + scheduleInTheFuture(schedSubject, f.tgt("b"), "body"); + + assertTrue(scheduleExists(f.stream, schedSubject)); + assertEquals(ScheduleManagement.Result.SUCCESS, + ScheduleManagement.cancelSchedule(jsm, schedSubject)); + assertFalse(scheduleExists(f.stream, schedSubject)); + } + + @Test + public void testCancelBySubject_noStreamThrows() { + String orphan = "no_such_subject_" + NUID.nextGlobalSequence(); + assertThrows(IllegalStateException.class, + () -> ScheduleManagement.cancelSchedule(jsm, orphan)); + } + + // -- cancelSchedule(jsm, scheduleSubject, scheduleStream) ------------------ + + @Test + public void testCancelBySubjectInStream_exact_subject() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("c"); + scheduleInTheFuture(schedSubject, f.tgt("c"), "body"); + + assertTrue(scheduleExists(f.stream, schedSubject)); + assertEquals(ScheduleManagement.Result.SUCCESS, + ScheduleManagement.cancelSchedule(jsm, schedSubject, f.stream)); + assertFalse(scheduleExists(f.stream, schedSubject)); + + assertEquals(ScheduleManagement.Result.NOT_FOUND, + ScheduleManagement.cancelSchedule(jsm, f.sched("nope"), f.stream)); + } + + @Test + public void testCancelBySubjectInStream_wildcard_purgesAll() throws Exception { + Fixture f = newFixture(); + String s1 = f.sched("w1"); + String s2 = f.sched("w2"); + String s3 = f.sched("w3"); + scheduleInTheFuture(s1, f.tgt("w1"), "1"); + scheduleInTheFuture(s2, f.tgt("w2"), "2"); + scheduleInTheFuture(s3, f.tgt("w3"), "3"); + + assertEquals(ScheduleManagement.Result.SUCCESS, + ScheduleManagement.cancelSchedule(jsm, f.schedPrefix + ".*", f.stream)); + assertFalse(scheduleExists(f.stream, s1)); + assertFalse(scheduleExists(f.stream, s2)); + assertFalse(scheduleExists(f.stream, s3)); + } + + @Test + public void testCancelBySubjectInStream_wildcard_noMatches() throws Exception { + Fixture f = newFixture(); + assertEquals(ScheduleManagement.Result.NOT_FOUND, + ScheduleManagement.cancelSchedule(jsm, f.schedPrefix + ".*", f.stream)); + } + + // -- publishAndCancelSchedule(jsm, sched, tgt, data, publishOnlyIfExists) -- + + @Test + public void testPublishAndCancel_unconditional_success() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("p1"); + String tgtSubject = f.tgt("p1"); + scheduleInTheFuture(schedSubject, tgtSubject, "body"); + + PublishAck ack = ScheduleManagement.publishAndCancelSchedule( + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null, false); + + assertNotNull(ack); + assertFalse(scheduleExists(f.stream, schedSubject)); + } + + @Test + public void testPublishAndCancel_ifExists_whenPresent() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("p2"); + String tgtSubject = f.tgt("p2"); + scheduleInTheFuture(schedSubject, tgtSubject, "body"); + + PublishAck ack = ScheduleManagement.publishAndCancelSchedule( + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null, true); + + assertNotNull(ack); + assertFalse(scheduleExists(f.stream, schedSubject)); + } + + @Test + public void testPublishAndCancel_ifExists_whenMissing() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("p3"); + String tgtSubject = f.tgt("p3"); + + PublishAck ack = ScheduleManagement.publishAndCancelSchedule( + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null, true); + + assertNull(ack); + } + + // -- publishAndCancelSchedule(jsm, sched, seq, tgt, data) ------------------ + + @Test + public void testPublishAndCancel_withSequence_success() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("p4"); + String tgtSubject = f.tgt("p4"); + long seq = scheduleInTheFuture(schedSubject, tgtSubject, "body"); + + PublishAck ack = ScheduleManagement.publishAndCancelSchedule( + jsm, schedSubject, seq, tgtSubject, "cancel-now".getBytes(), null); + + assertNotNull(ack); + assertFalse(scheduleExists(f.stream, schedSubject)); + } + + @Test + public void testPublishAndCancel_withSequence_wrongSequenceFails() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("p5"); + String tgtSubject = f.tgt("p5"); + long seq = scheduleInTheFuture(schedSubject, tgtSubject, "body"); + + assertThrows(JetStreamApiException.class, () -> + ScheduleManagement.publishAndCancelSchedule( + jsm, schedSubject, seq + 999, tgtSubject, "cancel-now".getBytes(), null)); + } +} From 3f2301277cd374d65a91d85f5d7961c42a1455c8 Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 12 May 2026 17:38:23 -0400 Subject: [PATCH 130/135] Add api docs --- .../io/synadia/examples/ScheduleBasics.java | 16 ++++++++++++++++ .../examples/ScheduleBasicsAlternate.java | 17 +++++++++++++++++ .../synadia/examples/ScheduleFromSource.java | 14 ++++++++++++++ .../io/synadia/examples/ScheduleUtils.java | 17 +++++++++++++++++ .../io/synadia/sm/PredefinedSchedules.java | 5 +++++ .../java/io/synadia/sm/ScheduleManagement.java | 18 ++++++++++++++++-- .../io/synadia/sm/ScheduledMessageBuilder.java | 8 +++++++- 7 files changed, 92 insertions(+), 3 deletions(-) diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java index ca7160e..0f3b6df 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java @@ -15,17 +15,33 @@ import static io.synadia.examples.ScheduleUtils.report; +/** + * Example: build and publish a few scheduled messages using + * {@link io.synadia.sm.ScheduledMessageBuilder#scheduleMessage(io.nats.client.JetStream)}. + */ public class ScheduleBasics { + + /** Stream name used by this example. */ public static final String STREAM = "schedules-enabled"; + /** Prefix for all schedule subjects in this example. */ public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ public static final String TARGET_PREFIX = "target."; private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; private static final String TARGETS = TARGET_PREFIX + "*"; + /** Subject patterns the example stream accepts. */ public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + private ScheduleBasics() {} + + /** + * Example entry point. + * @param args ignored + */ public static void main(String[] args) { try { Options options = new Options.Builder() diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java index 2cb4e35..15ebd84 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java @@ -15,17 +15,34 @@ import static io.synadia.examples.ScheduleUtils.report; +/** + * Example: same scenario as {@link ScheduleBasics}, but built using + * {@link io.synadia.sm.ScheduledMessageBuilder#build()} and then published + * via {@link io.nats.client.JetStream#publish(io.nats.client.Message)}. + */ public class ScheduleBasicsAlternate { + + /** Stream name used by this example. */ public static final String STREAM = "schedules-enabled"; + /** Prefix for all schedule subjects in this example. */ public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ public static final String TARGET_PREFIX = "target."; private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; private static final String TARGETS = TARGET_PREFIX + "*"; + /** Subject patterns the example stream accepts. */ public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + private ScheduleBasicsAlternate() {} + + /** + * Example entry point. + * @param args ignored + */ public static void main(String[] args) { try { Options options = new Options.Builder() diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java index 4062012..748a25e 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java @@ -16,15 +16,29 @@ import static io.synadia.examples.ScheduleUtils.report; +/** + * Example: schedule a message whose body and headers are taken from the last + * message published on a separate source subject (the + * {@code Nats-Schedule-Source} feature in ADR-51). + */ public class ScheduleFromSource { + + /** Stream name used by this example. */ public static final String STREAM = "schedules-enabled"; private static final String SCHEDULES = "schedules"; private static final String TARGET = "target"; private static final String SOURCE = "source"; + /** Subject patterns the example stream accepts. */ public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGET, SOURCE}; + private ScheduleFromSource() {} + + /** + * Example entry point. + * @param args ignored + */ public static void main(String[] args) { try { Options options = new Options.Builder() diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java index fac2489..1e7bc4b 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java @@ -6,8 +6,19 @@ import io.nats.client.Message; import io.nats.client.impl.Headers; +/** + * Small console-logging helpers shared by the example apps. + */ public class ScheduleUtils { + private ScheduleUtils() {} + + /** + * Print a pipe-separated, timestamped line to {@code System.out}. Each object is + * rendered with {@link Object#toString()}, except {@link Message}, which is rendered + * via {@link #toString(Message)}. + * @param objects the values to render + */ public static void report(Object... objects) { StringBuilder sb = new StringBuilder(); boolean first = true; @@ -28,6 +39,12 @@ public static void report(Object... objects) { System.out.println("[" + System.currentTimeMillis() + "] " + sb); } + /** + * Format a {@link Message} as a short multi-line string showing the subject, data + * (when present), and headers (when any). + * @param msg the message to format + * @return a human-readable representation + */ public static String toString(Message msg) { StringBuilder sb = new StringBuilder(System.lineSeparator()) .append(" Subject: ").append(msg.getSubject()); diff --git a/schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java b/schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java index 83f77fc..3bae59d 100644 --- a/schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java +++ b/schedule-message/src/main/java/io/synadia/sm/PredefinedSchedules.java @@ -3,6 +3,11 @@ package io.synadia.sm; +/** + * Predefined cron-like schedule shortcuts supported by NATS message schedules + * (per ADR-51). Pass one to + * {@link ScheduledMessageBuilder#schedule(PredefinedSchedules)}. + */ public enum PredefinedSchedules { /** * Run once a year, midnight, Jan. 1st. Same as Yearly. Equivalent to cron string 0 0 0 1 1 * diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java index 08914ec..2b51fb5 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java @@ -38,6 +38,9 @@ @NullMarked public abstract class ScheduleManagement { + /** Utility class — not intended to be instantiated. */ + private ScheduleManagement() {} + /** * Outcome of a {@code cancelSchedule(...)} call. */ @@ -189,7 +192,13 @@ public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubj * @param targetSubject the subject to publish to; this may be the * original schedule's target subject (to publish * early) or any other subject - * @param data the message body + * @param data the message body; may be {@code null} + * @param userHeaders extra headers to include on the published + * message; may be {@code null}. The + * {@code Nats-Scheduler} and + * {@code Nats-Schedule-Next} headers are always + * set by this method and override any conflicting + * keys from {@code userHeaders} * @param publishOnlyIfScheduleExists when {@code true}, the publish is sent with an * expected-last-subject-sequence guard so it * only succeeds if the schedule message is still @@ -236,7 +245,12 @@ public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubj * on {@code scheduleSubject} * @param targetSubject the subject to publish to (must differ from * {@code scheduleSubject}) - * @param data the message body + * @param data the message body; may be {@code null} + * @param userHeaders extra headers to include on the published message; + * may be {@code null}. The {@code Nats-Scheduler} and + * {@code Nats-Schedule-Next} headers are always set by + * this method and override any conflicting keys from + * {@code userHeaders} * @return the {@link PublishAck} from the server * @throws JetStreamApiException if the precondition fails or the server returned * another error diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java index d5d56b2..feda85f 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduledMessageBuilder.java @@ -59,6 +59,11 @@ public class ScheduledMessageBuilder { private boolean rollup; private final List sources = new ArrayList<>(); + /** + * Construct an empty builder. Configure the schedule and target subjects, the + * schedule (one of the {@code schedule*} methods), and any data / headers before + * calling {@link #build()} or {@link #scheduleMessage(JetStream)}. + */ public ScheduledMessageBuilder() {} /** @@ -149,7 +154,8 @@ public ScheduledMessageBuilder scheduleIn(Duration fromNow) { /** * Schedule for an amount of time from now. * This is not absolute since it takes time to build and send the message. - * @param fromNow how long from now to schedule + * @param fromNow how long from now to schedule, expressed in {@code timeUnit}s + * @param timeUnit the unit for {@code fromNow} * @return a ScheduledMessageBuilder object */ public ScheduledMessageBuilder scheduleIn(long fromNow, TimeUnit timeUnit) { From 46b032692eb56d407474e8a88e994fc8ee192e9b Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 12 May 2026 17:38:44 -0400 Subject: [PATCH 131/135] Update claude workflow --- .github/workflows/claude.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 5290a5f..bf8fd73 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,22 +1,33 @@ -name: Claude Code Review +name: Claude Code +# GITHUB_TOKEN needs contents:read and actions:read — required by +# claude-code-action for restoring trusted config files from the base branch. +# All other GitHub API access uses the App token. permissions: contents: read - pull-requests: write - issues: write - id-token: write + actions: read on: issue_comment: types: [created] pull_request_review_comment: types: [created] + pull_request_target: + types: [opened, reopened, ready_for_review] jobs: claude: + if: github.event_name != 'pull_request_target' || !contains(github.event.pull_request.body, '[skip claude]') uses: synadia-io/ai-workflows/.github/workflows/claude.yml@v2 with: gh_app_id: ${{ vars.CLAUDE_GH_APP_ID }} + checkout_mode: base + review_focus: | + Additionally focus on: + - Thread safety and proper synchronization (concurrent access, connection lifecycle) + - Exception handling patterns (checked vs unchecked, proper resource cleanup with try-with-resources) + - JetStream API correctness (subscription semantics, ack/nak behavior, consumer configuration) + - API compatibility with existing public interfaces secrets: claude_oauth_token: ${{ secrets.CLAUDE_OAUTH_TOKEN }} gh_app_private_key: ${{ secrets.CLAUDE_GH_APP_PRIVATE_KEY }} From ac8e0637f57f478007e719cac9135e7d08b256bc Mon Sep 17 00:00:00 2001 From: scottf Date: Tue, 12 May 2026 17:56:23 -0400 Subject: [PATCH 132/135] address review comments --- .../synadia/sm/ScheduleManagementTests.java | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java b/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java index f9d554b..561f1c1 100644 --- a/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java +++ b/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java @@ -9,6 +9,7 @@ import io.nats.client.api.MessageInfo; import io.nats.client.api.PublishAck; import io.nats.client.api.StorageType; +import io.nats.client.impl.Headers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -16,7 +17,7 @@ import java.time.Duration; import java.util.logging.Level; -import static io.nats.client.support.NatsJetStreamConstants.NATS_SCHEDULE_HDR; +import static io.nats.client.support.NatsJetStreamConstants.*; import static org.junit.jupiter.api.Assertions.*; public class ScheduleManagementTests { @@ -250,4 +251,61 @@ public void testPublishAndCancel_withSequence_wrongSequenceFails() throws Except ScheduleManagement.publishAndCancelSchedule( jsm, schedSubject, seq + 999, tgtSubject, "cancel-now".getBytes(), null)); } + + // -- ADR-51 constraint: targetSubject must not equal scheduleSubject ------- + + /** + * The server rejects an atomic stop publish whose target subject equals the schedule + * subject (ADR-51 error 10212). The client does not pre-check this — it lets the + * server surface the rejection. + */ + @Test + public void testPublishAndCancel_targetEqualsScheduleSubject_serverRejects() throws Exception { + Fixture f = newFixture(); + String sameSubject = f.sched("same"); + scheduleInTheFuture(sameSubject, f.tgt("same"), "body"); + + JetStreamApiException ex = assertThrows(JetStreamApiException.class, () -> + ScheduleManagement.publishAndCancelSchedule( + jsm, sameSubject, sameSubject, "x".getBytes(), null, false)); + assertEquals(10212, ex.getApiErrorCode()); + } + + // -- userHeaders cannot override the required Nats-Scheduler / Nats-Schedule-Next -- + + /** + * If the caller passes the protected headers ({@code Nats-Scheduler} or + * {@code Nats-Schedule-Next}) in {@code userHeaders}, the values set by the method + * must win — otherwise the atomic stop signal would be corrupted and the schedule + * would not be purged. Unrelated user headers must still be carried through to the + * published message. + */ + @Test + public void testPublishAndCancel_userHeadersCannotOverrideRequired() throws Exception { + Fixture f = newFixture(); + String schedSubject = f.sched("hdr"); + String tgtSubject = f.tgt("hdr"); + scheduleInTheFuture(schedSubject, tgtSubject, "body"); + + Headers userHeaders = new Headers(); + userHeaders.put(NATS_SCHEDULE_NEXT_HDR, "not-purge"); + userHeaders.put(NATS_SCHEDULER_HDR, "wrong-subject"); + userHeaders.put("X-Custom", "carry-me"); + + PublishAck ack = ScheduleManagement.publishAndCancelSchedule( + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), userHeaders, false); + assertNotNull(ack); + + // The schedule was actually cancelled — proves the required headers won. + assertFalse(scheduleExists(f.stream, schedSubject)); + + // The published target message carries the correct required values plus the user header. + MessageInfo tgtMsg = jsm.getLastMessage(f.stream, tgtSubject); + assertNotNull(tgtMsg); + Headers tgtHeaders = tgtMsg.getHeaders(); + assertNotNull(tgtHeaders); + assertEquals("purge", tgtHeaders.getFirst(NATS_SCHEDULE_NEXT_HDR)); + assertEquals(schedSubject, tgtHeaders.getFirst(NATS_SCHEDULER_HDR)); + assertEquals("carry-me", tgtHeaders.getFirst("X-Custom")); + } } From 40ecb868717e7accab816218424221b5f35756ef Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 13 May 2026 08:15:49 -0400 Subject: [PATCH 133/135] User headers first so internal headers win --- .../io/synadia/sm/ScheduleManagement.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java index 2b51fb5..4f01ab9 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java @@ -183,15 +183,18 @@ public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubj *

  1. {@code Nats-Scheduler}: {@code scheduleSubject}
  2. *
  3. {@code Nats-Schedule-Next}: {@code purge}
  4. * - * The {@code targetSubject} must not equal {@code scheduleSubject} — the server - * rejects such publishes with error code {@code 10212}. + * The {@code targetSubject} must not equal {@code scheduleSubject}. This constraint + * is enforced by the server, not by this method, so passing equal subjects surfaces + * as a {@link JetStreamApiException} with error code {@code 10212} from the publish + * call. * * @param jsm the JetStream management context (its * {@code jetStream()} context is used to publish) * @param scheduleSubject the schedule subject to stop * @param targetSubject the subject to publish to; this may be the * original schedule's target subject (to publish - * early) or any other subject + * early) or any other subject. Must not equal + * {@code scheduleSubject} — see above * @param data the message body; may be {@code null} * @param userHeaders extra headers to include on the published * message; may be {@code null}. The @@ -205,8 +208,10 @@ public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubj * present; when {@code false}, the publish is * sent unconditionally * @return the {@link PublishAck} from the server, or {@code null} when - * {@code publishOnlyIfScheduleExists} is {@code true} and no schedule for the - * subject could be located + * {@code publishOnlyIfScheduleExists} is {@code true} and the schedule for the + * subject could not be located. The lookup requires exactly one matching + * stream; if zero or more than one stream covers {@code scheduleSubject}, the + * method returns {@code null} without publishing * @throws JetStreamApiException if the server returned an error * @throws IOException if the request could not be sent */ @@ -243,8 +248,10 @@ public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubj * @param scheduleSubject the schedule subject to stop * @param scheduleStreamSequence the expected stream sequence of the schedule message * on {@code scheduleSubject} - * @param targetSubject the subject to publish to (must differ from - * {@code scheduleSubject}) + * @param targetSubject the subject to publish to. Must not equal + * {@code scheduleSubject}; the server enforces this + * constraint and rejects with error code {@code 10212} + * if the two are equal * @param data the message body; may be {@code null} * @param userHeaders extra headers to include on the published message; * may be {@code null}. The {@code Nats-Scheduler} and @@ -252,8 +259,9 @@ public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubj * this method and override any conflicting keys from * {@code userHeaders} * @return the {@link PublishAck} from the server - * @throws JetStreamApiException if the precondition fails or the server returned - * another error + * @throws JetStreamApiException if the precondition fails, the server returned error + * {@code 10212} (target subject equals schedule subject), or any other server + * error occurred * @throws IOException if the request could not be sent */ public static PublishAck publishAndCancelSchedule(JetStreamManagement jsm, String scheduleSubject, long scheduleStreamSequence, String targetSubject, @@ -269,13 +277,13 @@ public static PublishAck publishAndCancelSchedule(JetStreamManagement jsm, Strin private static Headers makeHeaders(String scheduleSubject, @Nullable Headers userHeaders) { Headers h = new Headers(); - h.put(NATS_SCHEDULE_NEXT_HDR, "purge"); - h.put(NATS_SCHEDULER_HDR, scheduleSubject); if (userHeaders != null) { for (String key : userHeaders.keySet()) { h.put(key, userHeaders.get(key)); } } + h.put(NATS_SCHEDULE_NEXT_HDR, "purge"); + h.put(NATS_SCHEDULER_HDR, scheduleSubject); return h; } From 2a3e45efc893b8f918aa4b00f640c53460319bdf Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 13 May 2026 18:14:09 -0400 Subject: [PATCH 134/135] Improve scheduling management and add examples --- .../io/synadia/examples/ScheduleBasics.java | 24 +-- .../examples/ScheduleBasicsAlternate.java | 22 ++- .../io/synadia/examples/ScheduleCancel.java | 117 ++++++++++++ .../io/synadia/examples/ScheduleCron.java | 107 +++++++++++ .../synadia/examples/ScheduleEveryAndIn.java | 128 +++++++++++++ ...leUtils.java => ScheduleExampleUtils.java} | 4 +- .../synadia/examples/ScheduleFromSource.java | 6 +- .../examples/ScheduleHeadersAndCopy.java | 112 ++++++++++++ .../synadia/examples/SchedulePredefined.java | 86 +++++++++ .../examples/SchedulePublishAndCancel.java | 169 ++++++++++++++++++ .../examples/ScheduleTtlAndRollup.java | 133 ++++++++++++++ .../io/synadia/sm/ScheduleManagement.java | 110 +++++++----- .../synadia/sm/ScheduleManagementTests.java | 17 +- 13 files changed, 961 insertions(+), 74 deletions(-) create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleCancel.java create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleCron.java create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleEveryAndIn.java rename schedule-message/src/examples/java/io/synadia/examples/{ScheduleUtils.java => ScheduleExampleUtils.java} (96%) create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleHeadersAndCopy.java create mode 100644 schedule-message/src/examples/java/io/synadia/examples/SchedulePredefined.java create mode 100644 schedule-message/src/examples/java/io/synadia/examples/SchedulePublishAndCancel.java create mode 100644 schedule-message/src/examples/java/io/synadia/examples/ScheduleTtlAndRollup.java diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java index 0f3b6df..f6d4ba6 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasics.java @@ -5,7 +5,6 @@ import io.nats.client.*; import io.nats.client.api.StorageType; -import io.nats.client.api.StreamInfo; import io.nats.client.support.DateTimeUtils; import io.synadia.sm.ScheduleManagement; import io.synadia.sm.ScheduledMessageBuilder; @@ -13,7 +12,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.synadia.examples.ScheduleUtils.report; +import static io.synadia.examples.ScheduleExampleUtils.report; /** * Example: build and publish a few scheduled messages using @@ -57,26 +56,25 @@ public static void main(String[] args) { try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} // Use the utility to properly create a schedulable stream - StreamInfo si = ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); - report("Created stream", si.getConfiguration()); + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); CountDownLatch latch = new CountDownLatch(4); Dispatcher d = connection.createDispatcher(); // subscribe to the subject that receives the schedule message js.subscribe(SCHEDULES, d, m -> { - report("SCHEDULED (received)", m); + report("MONITORING via '" + SCHEDULES + "'", m); m.ack(); }, false); // subscribe to the target subject js.subscribe(TARGETS, d, m -> { - report("TARGETED (received)", m); + report("TARGETED via '" + TARGETS + "'", m); m.ack(); latch.countDown(); }, false); - report("SCHEDULE-NOW (publishing)"); + report("SCHEDULING " + SCHEDULE_PREFIX + "now"); new ScheduledMessageBuilder() .scheduleSubject(SCHEDULE_PREFIX + "now") .targetSubject(TARGET_PREFIX + "now") @@ -84,7 +82,7 @@ public static void main(String[] args) { .data("Schedule-Now") .scheduleMessage(js); - report("SCHEDULE-AT (publishing)"); + report("SCHEDULING " + SCHEDULE_PREFIX + "at"); new ScheduledMessageBuilder() .scheduleSubject(SCHEDULE_PREFIX + "at") .targetSubject(TARGET_PREFIX + "at") @@ -92,15 +90,19 @@ public static void main(String[] args) { .data("Scheduled-At") .scheduleMessage(js); - report("SCHEDULE-EVERY (publishing)"); + report("SCHEDULING " + SCHEDULE_PREFIX + "every"); new ScheduledMessageBuilder() - .scheduleSubject(SCHEDULE_PREFIX + "at") - .targetSubject(TARGET_PREFIX + "at") + .scheduleSubject(SCHEDULE_PREFIX + "every") + .targetSubject(TARGET_PREFIX + "every") .scheduleEvery(1, TimeUnit.SECONDS) .data("Every Second") .scheduleMessage(js); latch.await(); + + // The "every" schedule keeps firing until it is removed. + report("CANCEL " + SCHEDULE_PREFIX + "every", + ScheduleManagement.cancelSchedule(jsm, SCHEDULE_PREFIX + "every", STREAM)); } } catch (Exception e) { diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java index 15ebd84..4822ee7 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java @@ -13,12 +13,14 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.synadia.examples.ScheduleUtils.report; +import static io.synadia.examples.ScheduleExampleUtils.report; /** * Example: same scenario as {@link ScheduleBasics}, but built using * {@link io.synadia.sm.ScheduledMessageBuilder#build()} and then published * via {@link io.nats.client.JetStream#publish(io.nats.client.Message)}. + * There is really no reason to do this unless you specifically want to log the + * actual message. */ public class ScheduleBasicsAlternate { @@ -66,13 +68,13 @@ public static void main(String[] args) { // subscribe to the subject that receives the schedule message js.subscribe(SCHEDULES, d, m -> { - report("SCHEDULED (received)", m); + report("MONITORING via '" + SCHEDULES + "'", m); m.ack(); }, false); // subscribe to the target subject js.subscribe(TARGETS, d, m -> { - report("TARGETED (received)", m); + report("TARGETED via '" + TARGETS + "'", m); m.ack(); latch.countDown(); }, false); @@ -83,7 +85,7 @@ public static void main(String[] args) { .scheduleImmediate() .data("Schedule-Now") .build(); - report("SCHEDULE-NOW (publishing)", m); + report("SCHEDULING " + SCHEDULE_PREFIX + "now", m); js.publish(m); m = new ScheduledMessageBuilder() @@ -92,19 +94,23 @@ public static void main(String[] args) { .scheduleAt(DateTimeUtils.gmtNow().plusSeconds(5)) .data("Scheduled-At") .build(); - report("SCHEDULE-AT (publishing)", m); + report("SCHEDULING " + SCHEDULE_PREFIX + "at", m); js.publish(m); m = new ScheduledMessageBuilder() - .scheduleSubject(SCHEDULE_PREFIX + "at") - .targetSubject(TARGET_PREFIX + "at") + .scheduleSubject(SCHEDULE_PREFIX + "every") + .targetSubject(TARGET_PREFIX + "every") .scheduleEvery(1, TimeUnit.SECONDS) .data("Every Second") .build(); - report("SCHEDULE-EVERY (publishing)", m); + report("SCHEDULING " + SCHEDULE_PREFIX + "now", m); js.publish(m); latch.await(); + + // The "every" schedule keeps firing until it is removed. + report("CANCEL " + SCHEDULE_PREFIX + "every", + ScheduleManagement.cancelSchedule(jsm, SCHEDULE_PREFIX + "every", STREAM)); } } catch (Exception e) { diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleCancel.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleCancel.java new file mode 100644 index 0000000..866fef7 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleCancel.java @@ -0,0 +1,117 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduleManagement.Result; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.time.Duration; + +import static io.synadia.examples.ScheduleExampleUtils.report; + +/** + * Example: stop a running schedule with each + * {@link ScheduleManagement#cancelSchedule cancelSchedule} overload. + *

    + *

      + *
    • by stream + sequence — the lowest-level call
    • + *
    • by stream + subject — looks up the sequence on a known stream
    • + *
    • by subject only — the helper locates the stream too
    • + *
    + * The example also shows a {@link Result#NOT_FOUND} outcome by cancelling a + * subject that no longer has a schedule. + */ +public class ScheduleCancel { + + /** Stream name used by this example. */ + public static final String STREAM = "schedules-enabled"; + + /** Prefix for all schedule subjects in this example. */ + public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + /** Subject patterns the example stream accepts. */ + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + private ScheduleCancel() {} + + /** + * Example entry point. + * @param args ignored + */ + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + + String bySeqSubject = SCHEDULE_PREFIX + "by-seq"; + String bySubjectSubject = SCHEDULE_PREFIX + "by-subject"; + String byLookupSubject = SCHEDULE_PREFIX + "by-lookup"; + + // Schedule each one an hour out so it can't fire while the example runs. + long bySeq = new ScheduledMessageBuilder() + .scheduleSubject(bySeqSubject) + .targetSubject(TARGET_PREFIX + "by-seq") + .scheduleIn(Duration.ofHours(1)) + .data("By-Seq") + .scheduleMessage(js); + report("SCHEDULED " + bySeqSubject + " at sequence " + bySeq); + + new ScheduledMessageBuilder() + .scheduleSubject(bySubjectSubject) + .targetSubject(TARGET_PREFIX + "by-subject") + .scheduleIn(Duration.ofHours(1)) + .data("By-Subject") + .scheduleMessage(js); + report("SCHEDULED " + bySubjectSubject); + + new ScheduledMessageBuilder() + .scheduleSubject(byLookupSubject) + .targetSubject(TARGET_PREFIX + "by-lookup") + .scheduleIn(Duration.ofHours(1)) + .data("By-Lookup") + .scheduleMessage(js); + report("SCHEDULED " + byLookupSubject); + + // 1) Sequence-based cancel. + Result r1 = ScheduleManagement.cancelSchedule(jsm, STREAM, bySeq); + report("CANCEL by sequence", r1); + + // 2) Subject + stream cancel. + Result r2 = ScheduleManagement.cancelSchedule(jsm, bySubjectSubject, STREAM); + report("CANCEL by subject + stream", r2); + + // 3) Subject-only cancel; the helper locates the stream. + Result r3 = ScheduleManagement.cancelSchedule(jsm, byLookupSubject); + report("CANCEL by subject (auto-find)", r3); + + // 4) Cancelling something that isn't there returns NOT_FOUND. + Result r4 = ScheduleManagement.cancelSchedule(jsm, bySeqSubject, STREAM); + report("CANCEL already-cancelled", r4); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleCron.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleCron.java new file mode 100644 index 0000000..55a1b39 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleCron.java @@ -0,0 +1,107 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.util.concurrent.CountDownLatch; + +import static io.synadia.examples.ScheduleExampleUtils.report; + +/** + * Example: schedule using cron expressions, including the + * {@link io.synadia.sm.ScheduledMessageBuilder#scheduleCron(String, String)} + * variant that takes an IANA time zone. + *

    + * NATS schedules use a six-field cron form (second minute hour day month + * day-of-week) per ADR-51. The expressions below fire on short intervals + * so the example completes quickly; the time-zone-bound expression behaves + * identically here because it does not pin a time of day, but the call shape + * is the same one you would use for {@code "0 30 9 * * *"} ("9:30 every day + * in New York"). + */ +public class ScheduleCron { + + /** Stream name used by this example. */ + public static final String STREAM = "schedules-enabled"; + + /** Prefix for all schedule subjects in this example. */ + public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + /** Subject patterns the example stream accepts. */ + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + private ScheduleCron() {} + + /** + * Example entry point. + * @param args ignored + */ + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + + CountDownLatch latch = new CountDownLatch(4); + Dispatcher d = connection.createDispatcher(); + + js.subscribe(TARGETS, d, m -> { + report("TARGETED via '" + TARGETS + "'", m); + m.ack(); + latch.countDown(); + }, false); + + String cronSubject = SCHEDULE_PREFIX + "cron"; + String cronTzSubject = SCHEDULE_PREFIX + "cron-tz"; + + // Six-field cron: every two seconds. + report("SCHEDULING " + cronSubject + " with cron '*/2 * * * * *'"); + new ScheduledMessageBuilder() + .scheduleSubject(cronSubject) + .targetSubject(TARGET_PREFIX + "cron") + .scheduleCron("*/2 * * * * *") + .data("Cron-Every-2s") + .scheduleMessage(js); + + // Same expression, evaluated in a specific IANA time zone. + report("SCHEDULING " + cronTzSubject + " with cron '*/3 * * * * *' (America/New_York)"); + new ScheduledMessageBuilder() + .scheduleSubject(cronTzSubject) + .targetSubject(TARGET_PREFIX + "cron-tz") + .scheduleCron("*/3 * * * * *", "America/New_York") + .data("Cron-Every-3s-NY") + .scheduleMessage(js); + + latch.await(); + + // Cron schedules keep firing until they are removed. + report("CANCEL " + cronSubject, ScheduleManagement.cancelSchedule(jsm, cronSubject, STREAM)); + report("CANCEL " + cronTzSubject, ScheduleManagement.cancelSchedule(jsm, cronTzSubject, STREAM)); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleEveryAndIn.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleEveryAndIn.java new file mode 100644 index 0000000..195abf4 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleEveryAndIn.java @@ -0,0 +1,128 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.synadia.examples.ScheduleExampleUtils.report; + +/** + * Example: the remaining {@code scheduleEvery(...)} and {@code scheduleIn(...)} + * overloads on {@link ScheduledMessageBuilder} that the {@link ScheduleBasics} + * example does not exercise: + *

      + *
    • {@link ScheduledMessageBuilder#scheduleEvery(String)} with a Go + * {@code time.ParseDuration} string ({@code "2s"}, {@code "1m30s"})
    • + *
    • {@link ScheduledMessageBuilder#scheduleEvery(Duration)}
    • + *
    • {@link ScheduledMessageBuilder#scheduleIn(Duration)} (one-shot)
    • + *
    • {@link ScheduledMessageBuilder#scheduleIn(long, TimeUnit)} (one-shot)
    • + *
    + */ +public class ScheduleEveryAndIn { + + /** Stream name used by this example. */ + public static final String STREAM = "schedules-enabled"; + + /** Prefix for all schedule subjects in this example. */ + public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + /** Subject patterns the example stream accepts. */ + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + private ScheduleEveryAndIn() {} + + /** + * Example entry point. + * @param args ignored + */ + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + + CountDownLatch latch = new CountDownLatch(6); + Dispatcher d = connection.createDispatcher(); + + js.subscribe(TARGETS, d, m -> { + report("TARGETED via '" + TARGETS + "'", m); + m.ack(); + latch.countDown(); + }, false); + + // @every using Go's time.ParseDuration syntax. + String everyStringSubject = SCHEDULE_PREFIX + "every-string"; + report("SCHEDULING " + everyStringSubject + " with scheduleEvery(\"2s\")"); + new ScheduledMessageBuilder() + .scheduleSubject(everyStringSubject) + .targetSubject(TARGET_PREFIX + "every-string") + .scheduleEvery("2s") + .data("Every-2s-String") + .scheduleMessage(js); + + // @every using a java.time.Duration. + String everyDurationSubject = SCHEDULE_PREFIX + "every-duration"; + report("SCHEDULING " + everyDurationSubject + " with scheduleEvery(Duration.ofSeconds(3))"); + new ScheduledMessageBuilder() + .scheduleSubject(everyDurationSubject) + .targetSubject(TARGET_PREFIX + "every-duration") + .scheduleEvery(Duration.ofSeconds(3)) + .data("Every-3s-Duration") + .scheduleMessage(js); + + // One-shot via Duration. + String inDurationSubject = SCHEDULE_PREFIX + "in-duration"; + report("SCHEDULING " + inDurationSubject + " with scheduleIn(Duration.ofSeconds(2))"); + new ScheduledMessageBuilder() + .scheduleSubject(inDurationSubject) + .targetSubject(TARGET_PREFIX + "in-duration") + .scheduleIn(Duration.ofSeconds(2)) + .data("In-2s-Duration") + .scheduleMessage(js); + + // One-shot via (long, TimeUnit). + String inUnitSubject = SCHEDULE_PREFIX + "in-unit"; + report("SCHEDULING " + inUnitSubject + " with scheduleIn(4, TimeUnit.SECONDS)"); + new ScheduledMessageBuilder() + .scheduleSubject(inUnitSubject) + .targetSubject(TARGET_PREFIX + "in-unit") + .scheduleIn(4, TimeUnit.SECONDS) + .data("In-4s-Unit") + .scheduleMessage(js); + + latch.await(); + + // The two recurring schedules will keep firing if not cancelled. + report("CANCEL " + everyStringSubject, ScheduleManagement.cancelSchedule(jsm, everyStringSubject, STREAM)); + report("CANCEL " + everyDurationSubject, ScheduleManagement.cancelSchedule(jsm, everyDurationSubject, STREAM)); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java similarity index 96% rename from schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java rename to schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java index 1e7bc4b..9eef648 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleUtils.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleExampleUtils.java @@ -9,9 +9,9 @@ /** * Small console-logging helpers shared by the example apps. */ -public class ScheduleUtils { +public class ScheduleExampleUtils { - private ScheduleUtils() {} + private ScheduleExampleUtils() {} /** * Print a pipe-separated, timestamped line to {@code System.out}. Each object is diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java index 748a25e..fab67f4 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleFromSource.java @@ -5,7 +5,6 @@ import io.nats.client.*; import io.nats.client.api.StorageType; -import io.nats.client.api.StreamInfo; import io.nats.client.impl.Headers; import io.nats.client.impl.NatsMessage; import io.synadia.sm.ScheduleManagement; @@ -14,7 +13,7 @@ import java.time.Duration; import java.util.concurrent.CountDownLatch; -import static io.synadia.examples.ScheduleUtils.report; +import static io.synadia.examples.ScheduleExampleUtils.report; /** * Example: schedule a message whose body and headers are taken from the last @@ -54,8 +53,7 @@ public static void main(String[] args) { try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} // Use the utility to properly create a schedulable stream - StreamInfo si = ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); - report("Created stream", si.getConfiguration()); + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); CountDownLatch latch1 = new CountDownLatch(1); CountDownLatch latch2 = new CountDownLatch(2); diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleHeadersAndCopy.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleHeadersAndCopy.java new file mode 100644 index 0000000..49fad3c --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleHeadersAndCopy.java @@ -0,0 +1,112 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.nats.client.impl.Headers; +import io.nats.client.impl.NatsMessage; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.util.concurrent.CountDownLatch; + +import static io.synadia.examples.ScheduleExampleUtils.report; + +/** + * Example: attaching user headers to a schedule and seeding a schedule from + * an existing {@link Message} via {@link ScheduledMessageBuilder#copy(Message)}. + *

    + * The builder writes its own {@code Nats-Schedule-*} headers after copying + * user headers, so user headers are preserved on the message that the schedule + * publishes to the target subject. + */ +public class ScheduleHeadersAndCopy { + + /** Stream name used by this example. */ + public static final String STREAM = "schedules-enabled"; + + /** Prefix for all schedule subjects in this example. */ + public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + /** Subject patterns the example stream accepts. */ + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + private ScheduleHeadersAndCopy() {} + + /** + * Example entry point. + * @param args ignored + */ + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + + CountDownLatch latch = new CountDownLatch(2); + Dispatcher d = connection.createDispatcher(); + + js.subscribe(TARGETS, d, m -> { + report("TARGETED via '" + TARGETS + "'", m); + m.ack(); + latch.countDown(); + }, false); + + // 1) Attach custom headers to a scheduled message. + Headers userHeaders = new Headers(); + userHeaders.put("X-Trace-Id", "abc-123"); + userHeaders.put("X-Origin", "ScheduleHeadersAndCopy"); + + String withHeadersSubject = SCHEDULE_PREFIX + "with-headers"; + report("SCHEDULING " + withHeadersSubject + " with custom headers"); + new ScheduledMessageBuilder() + .scheduleSubject(withHeadersSubject) + .targetSubject(TARGET_PREFIX + "with-headers") + .scheduleImmediate() + .headers(userHeaders) + .data("Has-User-Headers") + .scheduleMessage(js); + + // 2) Seed a schedule from an existing message via copy(). + // copy() pulls subject, data, and headers; the schedule subject + // here is taken from the source message, while the target subject + // is set explicitly. + Headers seedHeaders = new Headers(); + seedHeaders.put("X-Seeded-From", "template"); + Message seed = new NatsMessage(SCHEDULE_PREFIX + "copied", null, seedHeaders, "Seed-Data".getBytes()); + report("SEED message (template for copy())", seed); + + String copiedSubject = SCHEDULE_PREFIX + "copied"; + report("SCHEDULING " + copiedSubject + " built via copy(seed)"); + new ScheduledMessageBuilder() + .copy(seed) + .targetSubject(TARGET_PREFIX + "copied") + .scheduleImmediate() + .scheduleMessage(js); + + latch.await(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/SchedulePredefined.java b/schedule-message/src/examples/java/io/synadia/examples/SchedulePredefined.java new file mode 100644 index 0000000..46d2b00 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/SchedulePredefined.java @@ -0,0 +1,86 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.synadia.sm.PredefinedSchedules; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduledMessageBuilder; + +import static io.synadia.examples.ScheduleExampleUtils.report; + +/** + * Example: build a schedule for every entry of {@link PredefinedSchedules} + * ({@code @hourly}, {@code @daily}, {@code @weekly}, ...). The shortest + * predefined interval is {@code @hourly}, so this example would not fire + * within a reasonable test run; instead it publishes each schedule, reports + * what got stored on the stream, then cancels it. + */ +public class SchedulePredefined { + + /** Stream name used by this example. */ + public static final String STREAM = "schedules-enabled"; + + /** Prefix for all schedule subjects in this example. */ + public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + /** Subject patterns the example stream accepts. */ + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + private SchedulePredefined() {} + + /** + * Example entry point. + * @param args ignored + */ + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + + for (PredefinedSchedules p : PredefinedSchedules.values()) { + String scheduleSubject = SCHEDULE_PREFIX + p.name().toLowerCase(); + String targetSubject = TARGET_PREFIX + p.name().toLowerCase(); + + // Show what is being sent to the schedule subject. + Message m = new ScheduledMessageBuilder() + .scheduleSubject(scheduleSubject) + .targetSubject(targetSubject) + .schedule(p) + .data("Predefined-" + p.name()) + .build(); + report("SCHEDULING " + p.name(), m); + + js.publish(m); + + // Predefined schedules fire no more often than hourly, + // so cancel each one immediately to keep the stream tidy. + report("CANCEL " + scheduleSubject, + ScheduleManagement.cancelSchedule(jsm, scheduleSubject, STREAM)); + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/SchedulePublishAndCancel.java b/schedule-message/src/examples/java/io/synadia/examples/SchedulePublishAndCancel.java new file mode 100644 index 0000000..8fb6099 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/SchedulePublishAndCancel.java @@ -0,0 +1,169 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.PublishAck; +import io.nats.client.api.StorageType; +import io.nats.client.impl.Headers; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; + +import static io.synadia.examples.ScheduleExampleUtils.report; + +/** + * Example: the three atomic publish-and-stop calls on {@link ScheduleManagement}. + * Each one publishes a message to the {@code targetSubject} and stops the named + * schedule as a single atomic step. + *

      + *
    1. {@link ScheduleManagement#publishAndCancelSchedule(JetStreamManagement, String, String, byte[], Headers)} + * — unguarded; always publishes and returns a non-null ack
    2. + *
    3. {@link ScheduleManagement#publishAndCancelScheduleIfExists(JetStreamManagement, String, String, byte[], Headers)} + * — guarded; returns {@code null} when the schedule is no longer present
    4. + *
    5. {@link ScheduleManagement#publishAndCancelSchedule(JetStreamManagement, String, long, String, byte[], Headers)} + * — explicit-sequence overload, uses + * {@code Nats-Expected-Last-Subject-Sequence} so the publish only succeeds + * while the schedule message is still at the named sequence
    6. + *
    + * The example also calls the guarded form against an already-cancelled subject + * to show the {@code null} return. + */ +public class SchedulePublishAndCancel { + + /** Stream name used by this example. */ + public static final String STREAM = "schedules-enabled"; + + /** Prefix for all schedule subjects in this example. */ + public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + /** Subject patterns the example stream accepts. */ + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + private SchedulePublishAndCancel() {} + + /** + * Example entry point. + * @param args ignored + */ + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + + CountDownLatch latch = new CountDownLatch(3); + Dispatcher d = connection.createDispatcher(); + + js.subscribe(TARGETS, d, m -> { + report("TARGETED via '" + TARGETS + "'", m); + m.ack(); + latch.countDown(); + }, false); + + String unguardedSubject = SCHEDULE_PREFIX + "unguarded"; + String guardedSubject = SCHEDULE_PREFIX + "guarded"; + String bySeqSubject = SCHEDULE_PREFIX + "by-seq"; + + // Plant three schedules an hour out so none fire during the example. + new ScheduledMessageBuilder() + .scheduleSubject(unguardedSubject) + .targetSubject(TARGET_PREFIX + "unguarded") + .scheduleIn(Duration.ofHours(1)) + .data("placeholder") + .scheduleMessage(js); + + new ScheduledMessageBuilder() + .scheduleSubject(guardedSubject) + .targetSubject(TARGET_PREFIX + "guarded") + .scheduleIn(Duration.ofHours(1)) + .data("placeholder") + .scheduleMessage(js); + + long bySeq = new ScheduledMessageBuilder() + .scheduleSubject(bySeqSubject) + .targetSubject(TARGET_PREFIX + "by-seq") + .scheduleIn(Duration.ofHours(1)) + .data("placeholder") + .scheduleMessage(js); + + // 1) Unguarded form: publish + stop, no existence check. + // Always publishes and returns a non-null PublishAck (the call + // throws on publish failure), so no null check is needed. + PublishAck ack1 = ScheduleManagement.publishAndCancelSchedule( + jsm, + unguardedSubject, + TARGET_PREFIX + "unguarded", + "unguarded-payload".getBytes(), + null); + report("publishAndCancelSchedule (unguarded) seq=" + ack1.getSeqno()); + + // 2) Guarded form: looks up the schedule first. + // Returns null if the schedule is no longer present (or if the + // stream lookup is ambiguous), in which case nothing was published. + PublishAck ack2 = ScheduleManagement.publishAndCancelScheduleIfExists( + jsm, + guardedSubject, + TARGET_PREFIX + "guarded", + "guarded-payload".getBytes(), + null); + if (ack2 == null) { + report("publishAndCancelScheduleIfExists (present) - schedule not found, nothing published"); + } + else { + report("publishAndCancelScheduleIfExists (present) seq=" + ack2.getSeqno()); + } + + // 3) Explicit-sequence overload: uses Nats-Expected-Last-Subject-Sequence. + // Return type is non-nullable PublishAck; the call throws on a + // precondition or publish failure, so no null check is needed. + PublishAck ack3 = ScheduleManagement.publishAndCancelSchedule( + jsm, + bySeqSubject, + bySeq, + TARGET_PREFIX + "by-seq", + "by-seq-payload".getBytes(), + null); + report("publishAndCancelSchedule (by sequence) seq=" + ack3.getSeqno()); + + // 4) Guarded form against an already-cancelled subject -> null, no publish. + PublishAck ack4 = ScheduleManagement.publishAndCancelScheduleIfExists( + jsm, + unguardedSubject, + TARGET_PREFIX + "unguarded", + "should-not-be-sent".getBytes(), + null); + if (ack4 == null) { + report("publishAndCancelScheduleIfExists (gone) - null as expected, nothing published"); + } + else { + report("publishAndCancelScheduleIfExists (gone) unexpectedly published seq=" + ack4.getSeqno()); + } + + latch.await(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleTtlAndRollup.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleTtlAndRollup.java new file mode 100644 index 0000000..b72b2f7 --- /dev/null +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleTtlAndRollup.java @@ -0,0 +1,133 @@ +// Copyright (c) 2025-2026 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.*; +import io.nats.client.api.StorageType; +import io.synadia.sm.ScheduleManagement; +import io.synadia.sm.ScheduledMessageBuilder; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.synadia.examples.ScheduleExampleUtils.report; + +/** + * Example: the {@code Nats-Schedule-TTL} and {@code Nats-Schedule-Rollup} + * headers, set via {@link ScheduledMessageBuilder#messageTtl(MessageTtl)} and + * {@link ScheduledMessageBuilder#rollup()}. + *

    + *

      + *
    • TTL applies a per-message TTL to each message the schedule + * publishes; the stream must allow per-message TTLs (the schedulable + * stream helper turns this on for you).
    • + *
    • Rollup sets {@code Nats-Schedule-Rollup: sub}, which causes the + * target subject to be rolled up on each fire — only the latest + * message remains on the target subject.
    • + *
    + * After a few fires this example reports the per-subject stream counts so the + * difference between TTL'd and rolled-up targets is visible. + */ +public class ScheduleTtlAndRollup { + + /** Stream name used by this example. */ + public static final String STREAM = "schedules-enabled"; + + /** Prefix for all schedule subjects in this example. */ + public static final String SCHEDULE_PREFIX = "schedule."; + + /** Prefix for all target subjects in this example. */ + public static final String TARGET_PREFIX = "target."; + + private static final String SCHEDULES = SCHEDULE_PREFIX + ">"; + private static final String TARGETS = TARGET_PREFIX + "*"; + + /** Subject patterns the example stream accepts. */ + public static final String[] STREAM_SUBJECTS = new String[]{SCHEDULES, TARGETS}; + + private ScheduleTtlAndRollup() {} + + /** + * Example entry point. + * @param args ignored + */ + public static void main(String[] args) { + try { + Options options = new Options.Builder() + .server("nats://localhost:4222") + .errorListener(new ErrorListener() {}) + .build(); + + try (Connection connection = Nats.connect(options)) { + JetStreamManagement jsm = connection.jetStreamManagement(); + JetStream js = connection.jetStream(); + + // delete the stream in case it existed, just for a fresh example + try { jsm.deleteStream(STREAM); } catch (Exception ignore) {} + + ScheduleManagement.createSchedulableStream(jsm, STREAM, StorageType.Memory, STREAM_SUBJECTS); + + String ttlSubject = SCHEDULE_PREFIX + "ttl"; + String rollupSubject = SCHEDULE_PREFIX + "rollup"; + String ttlTarget = TARGET_PREFIX + "ttl"; + String rollupTarget = TARGET_PREFIX + "rollup"; + + AtomicInteger ttlHits = new AtomicInteger(); + AtomicInteger rollupHits = new AtomicInteger(); + CountDownLatch latch = new CountDownLatch(6); + Dispatcher d = connection.createDispatcher(); + + js.subscribe(TARGETS, d, m -> { + if (m.getSubject().equals(ttlTarget)) ttlHits.incrementAndGet(); + if (m.getSubject().equals(rollupTarget)) rollupHits.incrementAndGet(); + report("TARGETED via '" + TARGETS + "'", m); + m.ack(); + latch.countDown(); + }, false); + + // Each published message gets a 3-second TTL on the stream. + report("SCHEDULING " + ttlSubject + " with messageTtl(3s)"); + new ScheduledMessageBuilder() + .scheduleSubject(ttlSubject) + .targetSubject(ttlTarget) + .scheduleEvery(2, TimeUnit.SECONDS) + .messageTtl(MessageTtl.seconds(3)) + .data("TTL-3s") + .scheduleMessage(js); + + // Each fire replaces any prior message on the target subject. + report("SCHEDULING " + rollupSubject + " with rollup()"); + new ScheduledMessageBuilder() + .scheduleSubject(rollupSubject) + .targetSubject(rollupTarget) + .scheduleEvery(2, TimeUnit.SECONDS) + .rollup() + .data("Rollup") + .scheduleMessage(js); + + latch.await(); + + report("CANCEL " + ttlSubject, ScheduleManagement.cancelSchedule(jsm, ttlSubject, STREAM)); + report("CANCEL " + rollupSubject, ScheduleManagement.cancelSchedule(jsm, rollupSubject, STREAM)); + + report("DELIVERED to " + ttlTarget, ttlHits.get()); + report("DELIVERED to " + rollupTarget, rollupHits.get()); + + // Both targets received the same number of fires, but the rollup + // target retains only the latest message; the TTL target retains + // each published message until it expires after 3s. + report("STREAM count for " + ttlTarget, + jsm.getStreamInfo(STREAM, io.nats.client.api.StreamInfoOptions.filterSubjects(ttlTarget)) + .getStreamState().getSubjectCount()); + report("STREAM count for " + rollupTarget, + jsm.getStreamInfo(STREAM, io.nats.client.api.StreamInfoOptions.filterSubjects(rollupTarget)) + .getStreamState().getSubjectCount()); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java index 4f01ab9..5613e09 100644 --- a/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java +++ b/schedule-message/src/main/java/io/synadia/sm/ScheduleManagement.java @@ -25,10 +25,15 @@ * longer fire. {@link #cancelSchedule(JetStreamManagement, String, long)} deletes by * stream sequence; the subject-based overloads look the sequence up first. *
  5. Atomic publish-and-stop — publish a message to a different subject and stop - * the schedule as a single atomic step, optionally guarded by an existence check on - * the schedule message. See - * {@link #publishAndCancelSchedule(JetStreamManagement, String, String, byte[], Headers, boolean)} - * and {@link #publishAndCancelSchedule(JetStreamManagement, String, long, String, byte[], Headers)}.
  6. + * the schedule as a single atomic step. The unconditional form + * {@link #publishAndCancelSchedule(JetStreamManagement, String, String, byte[], Headers)} + * publishes without checking that the schedule still exists; + * {@link #publishAndCancelScheduleIfExists(JetStreamManagement, String, String, byte[], Headers)} + * guards the publish with an existence check, returning {@code null} if the schedule + * is no longer present. The sequence-bound variant + * {@link #publishAndCancelSchedule(JetStreamManagement, String, long, String, byte[], Headers)} + * uses {@code Nats-Expected-Last-Subject-Sequence} so the publish only succeeds while + * the schedule message is still at the named sequence. * * Per the ADR the publish subject of the atomic variants must not equal the schedule * subject; the server rejects such publishes with error code {@code 10212}. @@ -183,55 +188,78 @@ public static Result cancelSchedule(JetStreamManagement jsm, String scheduleSubj *
  7. {@code Nats-Scheduler}: {@code scheduleSubject}
  8. *
  9. {@code Nats-Schedule-Next}: {@code purge}
  10. * + * The publish is sent unconditionally; the schedule is stopped as a side effect of + * the server processing the headers. Use + * {@link #publishAndCancelScheduleIfExists(JetStreamManagement, String, String, byte[], Headers)} + * if you need to skip the publish when the schedule is no longer present. + *

    * The {@code targetSubject} must not equal {@code scheduleSubject}. This constraint * is enforced by the server, not by this method, so passing equal subjects surfaces * as a {@link JetStreamApiException} with error code {@code 10212} from the publish * call. * - * @param jsm the JetStream management context (its - * {@code jetStream()} context is used to publish) - * @param scheduleSubject the schedule subject to stop - * @param targetSubject the subject to publish to; this may be the - * original schedule's target subject (to publish - * early) or any other subject. Must not equal - * {@code scheduleSubject} — see above - * @param data the message body; may be {@code null} - * @param userHeaders extra headers to include on the published - * message; may be {@code null}. The - * {@code Nats-Scheduler} and - * {@code Nats-Schedule-Next} headers are always - * set by this method and override any conflicting - * keys from {@code userHeaders} - * @param publishOnlyIfScheduleExists when {@code true}, the publish is sent with an - * expected-last-subject-sequence guard so it - * only succeeds if the schedule message is still - * present; when {@code false}, the publish is - * sent unconditionally - * @return the {@link PublishAck} from the server, or {@code null} when - * {@code publishOnlyIfScheduleExists} is {@code true} and the schedule for the - * subject could not be located. The lookup requires exactly one matching - * stream; if zero or more than one stream covers {@code scheduleSubject}, the - * method returns {@code null} without publishing + * @param jsm the JetStream management context (its {@code jetStream()} + * context is used to publish) + * @param scheduleSubject the schedule subject to stop + * @param targetSubject the subject to publish to; this may be the original + * schedule's target subject (to publish early) or any other + * subject. Must not equal {@code scheduleSubject} — see above + * @param data the message body; may be {@code null} + * @param userHeaders extra headers to include on the published message; may be + * {@code null}. The {@code Nats-Scheduler} and + * {@code Nats-Schedule-Next} headers are always set by this + * method and override any conflicting keys from + * {@code userHeaders} + * @return the {@link PublishAck} from the server * @throws JetStreamApiException if the server returned an error * @throws IOException if the request could not be sent */ - public static @Nullable PublishAck publishAndCancelSchedule(JetStreamManagement jsm, String scheduleSubject, String targetSubject, - byte @Nullable[] data, @Nullable Headers userHeaders, boolean publishOnlyIfScheduleExists) throws JetStreamApiException, IOException { - if (publishOnlyIfScheduleExists) { - String streamName = findStreamLenient(jsm, scheduleSubject); - if (streamName != null) { - long seq = getScheduleSequence(jsm, streamName, scheduleSubject); - if (seq != -1) { - return publishAndCancelSchedule(jsm, scheduleSubject, seq, targetSubject, data, userHeaders); - } - } - return null; - } - + public static PublishAck publishAndCancelSchedule(JetStreamManagement jsm, String scheduleSubject, String targetSubject, + byte @Nullable[] data, @Nullable Headers userHeaders) throws JetStreamApiException, IOException { Headers h = makeHeaders(scheduleSubject, userHeaders); return jsm.jetStream().publish(targetSubject, h, data); } + /** + * Guarded variant of + * {@link #publishAndCancelSchedule(JetStreamManagement, String, String, byte[], Headers)}: + * looks up the schedule message first and only publishes if it is still present, using + * the {@code Nats-Expected-Last-Subject-Sequence} precondition so the operation is + * atomic with any concurrent fire. + *

    + * The lookup requires exactly one stream to cover {@code scheduleSubject}; if + * zero or more than one stream matches, the method returns {@code null} without + * publishing. + * + * @param jsm the JetStream management context + * @param scheduleSubject the schedule subject to stop + * @param targetSubject the subject to publish to; must not equal + * {@code scheduleSubject} (server rejects with code + * {@code 10212}) + * @param data the message body; may be {@code null} + * @param userHeaders extra headers to include on the published message; may be + * {@code null}. The {@code Nats-Scheduler} and + * {@code Nats-Schedule-Next} headers are always set by this + * method and override any conflicting keys from + * {@code userHeaders} + * @return the {@link PublishAck} from the server, or {@code null} if the schedule + * for {@code scheduleSubject} could not be located (no schedule message present, + * or the stream lookup was ambiguous) + * @throws JetStreamApiException if the server returned an error + * @throws IOException if the request could not be sent + */ + public static @Nullable PublishAck publishAndCancelScheduleIfExists(JetStreamManagement jsm, String scheduleSubject, String targetSubject, + byte @Nullable[] data, @Nullable Headers userHeaders) throws JetStreamApiException, IOException { + String streamName = findStreamLenient(jsm, scheduleSubject); + if (streamName != null) { + long seq = getScheduleSequence(jsm, streamName, scheduleSubject); + if (seq != -1) { + return publishAndCancelSchedule(jsm, scheduleSubject, seq, targetSubject, data, userHeaders); + } + } + return null; + } + /** * Atomic publish-and-stop guarded by an explicit existence check. Same headers as * the simpler overload, but additionally sets: diff --git a/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java b/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java index 561f1c1..fc630bd 100644 --- a/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java +++ b/schedule-message/src/test/java/io/synadia/sm/ScheduleManagementTests.java @@ -182,7 +182,8 @@ public void testCancelBySubjectInStream_wildcard_noMatches() throws Exception { ScheduleManagement.cancelSchedule(jsm, f.schedPrefix + ".*", f.stream)); } - // -- publishAndCancelSchedule(jsm, sched, tgt, data, publishOnlyIfExists) -- + // -- publishAndCancelSchedule(jsm, sched, tgt, data, userHeaders) ---------- + // -- publishAndCancelScheduleIfExists(jsm, sched, tgt, data, userHeaders) -- @Test public void testPublishAndCancel_unconditional_success() throws Exception { @@ -192,7 +193,7 @@ public void testPublishAndCancel_unconditional_success() throws Exception { scheduleInTheFuture(schedSubject, tgtSubject, "body"); PublishAck ack = ScheduleManagement.publishAndCancelSchedule( - jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null, false); + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null); assertNotNull(ack); assertFalse(scheduleExists(f.stream, schedSubject)); @@ -205,8 +206,8 @@ public void testPublishAndCancel_ifExists_whenPresent() throws Exception { String tgtSubject = f.tgt("p2"); scheduleInTheFuture(schedSubject, tgtSubject, "body"); - PublishAck ack = ScheduleManagement.publishAndCancelSchedule( - jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null, true); + PublishAck ack = ScheduleManagement.publishAndCancelScheduleIfExists( + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null); assertNotNull(ack); assertFalse(scheduleExists(f.stream, schedSubject)); @@ -218,8 +219,8 @@ public void testPublishAndCancel_ifExists_whenMissing() throws Exception { String schedSubject = f.sched("p3"); String tgtSubject = f.tgt("p3"); - PublishAck ack = ScheduleManagement.publishAndCancelSchedule( - jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null, true); + PublishAck ack = ScheduleManagement.publishAndCancelScheduleIfExists( + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), null); assertNull(ack); } @@ -267,7 +268,7 @@ public void testPublishAndCancel_targetEqualsScheduleSubject_serverRejects() thr JetStreamApiException ex = assertThrows(JetStreamApiException.class, () -> ScheduleManagement.publishAndCancelSchedule( - jsm, sameSubject, sameSubject, "x".getBytes(), null, false)); + jsm, sameSubject, sameSubject, "x".getBytes(), null)); assertEquals(10212, ex.getApiErrorCode()); } @@ -293,7 +294,7 @@ public void testPublishAndCancel_userHeadersCannotOverrideRequired() throws Exce userHeaders.put("X-Custom", "carry-me"); PublishAck ack = ScheduleManagement.publishAndCancelSchedule( - jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), userHeaders, false); + jsm, schedSubject, tgtSubject, "cancel-now".getBytes(), userHeaders); assertNotNull(ack); // The schedule was actually cancelled — proves the required headers won. From 7ad9286e6460ee347e8d6b0b957c09d5c0ed6875 Mon Sep 17 00:00:00 2001 From: scottf Date: Wed, 13 May 2026 20:27:58 -0400 Subject: [PATCH 135/135] addressed example word change --- .../java/io/synadia/examples/ScheduleBasicsAlternate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java index 4822ee7..b7fc51a 100644 --- a/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java +++ b/schedule-message/src/examples/java/io/synadia/examples/ScheduleBasicsAlternate.java @@ -103,7 +103,7 @@ public static void main(String[] args) { .scheduleEvery(1, TimeUnit.SECONDS) .data("Every Second") .build(); - report("SCHEDULING " + SCHEDULE_PREFIX + "now", m); + report("SCHEDULING " + SCHEDULE_PREFIX + "every", m); js.publish(m); latch.await();