diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a91b910..9710cca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ The format is inspired by Keep a Changelog, and this project adheres to semantic - `BaseKey` now directly implements `Serializable` (previously implemented the now-deleted `IKey` interface). - `Nip05Validator` now creates `HttpClient` instances directly via a `Function` factory (previously used deleted `HttpClientProvider`/`DefaultHttpClientProvider` interface). +## [2.0.1] - 2026-05-06 + +### Fixed +- `NostrRelayClient` log statements were emitting `Sending request to relay null: ...` (and similar) once the WebSocket session had closed, because the relay URI was being read via `clientSession.getUri()` which returns `null` after close. Captured the URI in a `private final String relayUri` field set in each constructor and replaced 8 `clientSession.getUri()` log call-sites. Resolves 188 occurrences per 2-hour window observed in a downstream consumer's staging logs ([#523](https://github.com/tcheeric/nostr-java/pull/523)). + ## [2.0.0] - 2026-02-24 This is a major release that implements the full design simplification described in `docs/developer/SIMPLIFICATION_PROPOSAL.md`, reducing the library from 9 modules with ~180 classes to 4 modules with ~40 classes. diff --git a/nostr-java-client/pom.xml b/nostr-java-client/pom.xml index 143ef9ba..07aab89b 100644 --- a/nostr-java-client/pom.xml +++ b/nostr-java-client/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.0.0 + 2.0.1 ../pom.xml diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/NostrRelayClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/NostrRelayClient.java index e1d409c1..4c893991 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/NostrRelayClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/NostrRelayClient.java @@ -72,6 +72,13 @@ public class NostrRelayClient extends TextWebSocketHandler implements AutoClosea private int maxEventsPerRequest = DEFAULT_MAX_EVENTS_PER_REQUEST; private final WebSocketSession clientSession; + /** + * Relay URI captured at construction time. Used for logging so that + * messages remain meaningful even after the underlying WebSocket session + * has been closed (at which point {@link WebSocketSession#getUri()} may + * return {@code null} on some implementations). + */ + private final String relayUri; private final ReentrantLock sendLock = new ReentrantLock(); private PendingRequest pendingRequest; private final Map listeners = new ConcurrentHashMap<>(); @@ -119,6 +126,7 @@ int eventCount() { public NostrRelayClient(@Value("${nostr.relay.uri}") String relayUri) throws java.util.concurrent.ExecutionException, InterruptedException { + this.relayUri = relayUri; this.clientSession = connectSession(relayUri); connectionState.set(ConnectionState.CONNECTED); } @@ -129,6 +137,7 @@ public NostrRelayClient(String relayUri, long awaitTimeoutMs) throw new IllegalArgumentException("awaitTimeoutMs must be positive"); } this.awaitTimeoutMs = awaitTimeoutMs; + this.relayUri = relayUri; log.info("NostrRelayClient created for {} with awaitTimeoutMs={}", relayUri, awaitTimeoutMs); this.clientSession = connectSession(relayUri); connectionState.set(ConnectionState.CONNECTED); @@ -143,6 +152,14 @@ public NostrRelayClient(String relayUri, long awaitTimeoutMs) } this.clientSession = clientSession; this.awaitTimeoutMs = awaitTimeoutMs; + URI sessionUri = null; + try { + sessionUri = clientSession.getUri(); + } catch (Exception ignored) { + // Some WebSocketSession implementations may throw before the session + // is fully initialised; fall through and store null. + } + this.relayUri = sessionUri == null ? null : sessionUri.toString(); connectionState.set(ConnectionState.CONNECTED); } @@ -253,7 +270,7 @@ public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull Cl public List send(T eventMessage) throws IOException { String json = eventMessage.encode(); log.debug("Sending {} to relay {} (size={} bytes)", - eventMessage.getCommand(), clientSession.getUri(), json.length()); + eventMessage.getCommand(), relayUri, json.length()); return send(json); } @@ -269,7 +286,7 @@ public List send(String json) throws IOException { } request = new PendingRequest(maxEventsPerRequest); pendingRequest = request; - log.info("Sending request to relay {}: {}", clientSession.getUri(), json); + log.info("Sending request to relay {}: {}", relayUri, json); clientSession.sendMessage(new TextMessage(json)); } finally { sendLock.unlock(); @@ -280,7 +297,7 @@ public List send(String json) throws IOException { try { List result = request.getFuture().get(timeout, TimeUnit.MILLISECONDS); - log.info("Received {} relay events via {}", result.size(), clientSession.getUri()); + log.info("Received {} relay events via {}", result.size(), relayUri); return result; } catch (TimeoutException e) { log.error("Timed out waiting for relay response after {}ms", timeout); @@ -350,7 +367,7 @@ public AutoCloseable subscribe( throws IOException { String json = requestMessage.encode(); log.debug("Subscribing with {} on relay {} (size={} bytes)", - requestMessage.getCommand(), clientSession.getUri(), json.length()); + requestMessage.getCommand(), relayUri, json.length()); return subscribe(json, messageListener, errorListener, closeListener); } @@ -425,7 +442,7 @@ public CompletableFuture subscribeAsync( @Recover public List recover(IOException ex, String json) throws IOException { log.error("Failed to send message to relay {} after retries (size={} bytes)", - clientSession.getUri(), json.length(), ex); + relayUri, json.length(), ex); throw ex; } @@ -433,7 +450,7 @@ public List recover(IOException ex, String json) throws IOException { public List recover(IOException ex, BaseMessage eventMessage) throws IOException { String json = eventMessage.encode(); log.error("Failed to send {} to relay {} after retries (size={} bytes)", - eventMessage.getCommand(), clientSession.getUri(), json.length(), ex); + eventMessage.getCommand(), relayUri, json.length(), ex); throw ex; } @@ -446,7 +463,7 @@ public AutoCloseable recoverSubscription( Runnable closeListener) throws IOException { log.error("Failed to subscribe on relay {} after retries (size={} bytes)", - clientSession.getUri(), json.length(), ex); + relayUri, json.length(), ex); throw ex; } @@ -460,7 +477,7 @@ public AutoCloseable recoverSubscription( throws IOException { String json = requestMessage.encode(); log.error("Failed to subscribe with {} on relay {} after retries (size={} bytes)", - requestMessage.getCommand(), clientSession.getUri(), json.length(), ex); + requestMessage.getCommand(), relayUri, json.length(), ex); throw ex; } diff --git a/nostr-java-core/pom.xml b/nostr-java-core/pom.xml index 2305d2fd..028f7cc7 100644 --- a/nostr-java-core/pom.xml +++ b/nostr-java-core/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.0.0 + 2.0.1 ../pom.xml diff --git a/nostr-java-event/pom.xml b/nostr-java-event/pom.xml index 0fc4b5f0..63139968 100644 --- a/nostr-java-event/pom.xml +++ b/nostr-java-event/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.0.0 + 2.0.1 ../pom.xml diff --git a/nostr-java-identity/pom.xml b/nostr-java-identity/pom.xml index 097c5641..68f41a56 100644 --- a/nostr-java-identity/pom.xml +++ b/nostr-java-identity/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.0.0 + 2.0.1 ../pom.xml diff --git a/pom.xml b/pom.xml index 92a0b6e0..2e2d494e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ xyz.tcheeric nostr-java - 2.0.0 + 2.0.1 pom nostr-java