Skip to content

Model#2

Open
Rickank wants to merge 13 commits into
mainfrom
model
Open

Model#2
Rickank wants to merge 13 commits into
mainfrom
model

Conversation

@Rickank

@Rickank Rickank commented Nov 14, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Full chat experience: real-time send/receive, message bubbles, topic/room switching, auto-scroll, input validation, and renamed window title/layout ("RuneChat") with a larger default window.
  • Style

    • Polished stylesheet for chat UI, headers, message bubbles, controls, and scrollbars.
  • Tests

    • Comprehensive unit and integration tests with mocks and simulated server scenarios.
  • Chores

    • Environment config support, JSON handling, HTTP messaging backend, module/dependency updates, and .env added to .gitignore.

@coderabbitai

coderabbitai Bot commented Nov 14, 2025

Copy link
Copy Markdown

Walkthrough

Adds a chat application ("RuneChat"): new Ntfy connection interface and HttpClient/Jackson implementation, a stateful HelloModel with async send/receive, JavaFX UI and controller (chat bubbles, topic management), FxUtils helper, dotenv support, module/pom updates, UI stylesheet and extensive tests including a WireMock integration.

Changes

Cohort / File(s) Summary
Build & Module
\.gitignore, pom.xml, src/main/java/module-info.java
Ignore .env; add dependencies dotenv-java, jackson-databind, and wiremock (test); update module requires for dotenv, java.net.http, Jackson, and javafx.graphics.
Connection & DTO
src/main/java/com/example/NtfyConnection.java, src/main/java/com/example/NtfyConnectionImpl.java, src/main/java/com/example/NtfyMessageDto.java
Add NtfyConnection interface (send/receive + defaults); implement NtfyConnectionImpl using HttpClient, Jackson, and dotenv for async send/receive and streaming JSON parsing; add NtfyMessageDto record with @JsonIgnoreProperties.
Model & Utils
src/main/java/com/example/HelloModel.java, src/main/java/com/example/FxUtils.java
Introduce HelloModel managing connection, observable messages, topic and input properties, async send/receive, and FX-thread updates via FxUtils.runOnFx.
Controller & App
src/main/java/com/example/HelloController.java, src/main/java/com/example/HelloFX.java
Extend controller with ListView, inputs, topic changer, custom ListCell rendering for chat bubbles, async send handling; update app title, scene size, and stylesheet loading.
UI Resources
src/main/resources/com/example/hello-view.fxml, src/main/resources/com/example/style.css
Replace UI layout with VBox-based chat view (header, message list, topic changer, input area); add comprehensive CSS for chat styling and bubbles.
Tests & Test Helpers
src/test/java/com/example/HelloModelTest.java, src/test/java/com/example/NtfyConnectionSpy.java
Add JUnit 5 tests covering sends, receives, validation, error paths, concurrency, and WireMock integration; add NtfyConnectionSpy test helper for simulating sends/incoming messages.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant UI as HelloController
    participant M as HelloModel
    participant C as NtfyConnectionImpl
    participant S as Server

    rect rgb(235,245,255)
    Note over U,UI: Send message (async)
    U->>UI: Click Send
    UI->>M: sendMessageAsync(callback)
    M->>C: send(message, callback)
    C->>S: POST /{topic} (body, X-User-Id)
    S-->>C: 2xx / error
    C-->>M: callback(success)
    M->>UI: runOnFx(update UI)
    end

    rect rgb(235,255,240)
    Note over S,M: Receive stream (async)
    M->>C: receive(handler)
    C->>S: GET /{topic}/json (stream)
    S-->>C: event lines
    C->>C: parse -> NtfyMessageDto
    C->>M: handler.accept(message)
    M->>UI: runOnFx(add to ObservableList)
    UI->>U: ListView renders message
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay attention to:
    • src/main/java/com/example/NtfyConnectionImpl.java — async HTTP streaming, JSON parsing, error handling, thread usage.
    • src/main/java/com/example/HelloModel.java — concurrency, FX-thread transitions, property lifecycle and topic switching.
    • src/main/java/com/example/HelloController.java — FXML bindings, ListCell rendering and UI-thread interactions.
    • src/test/java/com/example/HelloModelTest.java — asynchronous test synchronization and WireMock integration.

Poem

🐰 I hopped into code with a twitching nose,
Bubbles and topics where the chatlight glows,
Async hops, dotenv dreams, a server cheer,
Messages tumble — the rabbit steers,
RuneChat hums beneath my floppy ears.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.90% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Model' is vague and generic. While it relates to model-related changes in the PR, it does not clearly convey the primary purpose or scope of the substantial changeset. Use a more descriptive title that captures the main objective, such as 'Add Ntfy chat integration with message model and UI components' or 'Implement Ntfy connection and chat messaging system'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch model

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (8)
src/main/java/com/example/ManyParametersBuilder.java (1)

3-37: Builder implementation is correct.

The builder pattern is properly implemented with fluent API. However, ensure the ManyParameters constructor is fixed to actually store the values (see comments on ManyParameters.java), otherwise this builder will create objects that lose all their data.

Consider adding validation for required fields or constraints:

 public ManyParameters createManyParameters() {
+    if (computerName == null || computerName.isEmpty()) {
+        throw new IllegalStateException("computerName is required");
+    }
+    if (timeout < 0) {
+        throw new IllegalStateException("timeout must be non-negative");
+    }
     return new ManyParameters(computerName, timeout, method, size, data);
 }
pom.xml (1)

58-63: Consider upgrading WireMock to the latest stable version 3.13.1.

The latest stable release is WireMock 3.13.1, whereas the current code uses 4.0.0-beta.15. WireMock 4.x is available only as beta. While scoped to tests, upgrading to the stable 3.13.1 release will avoid potential instability or unexpected behavior from pre-release versions.

src/main/java/com/example/HelloController.java (2)

13-20: Decouple controller from concrete NtfyConnectionImpl for testability and configuration

Instantiating HelloModel with new NtfyConnectionImpl() in the controller hard‑wires the real HTTP backend and .env/HOST_NAME requirement into FXML loading. This makes the controller harder to unit‑test and less flexible (e.g. different hosts, spies, or mock implementations).

Consider injecting HelloModel (or NtfyConnection) from the application bootstrap instead of constructing it here, e.g. via constructor/setter injection or a simple factory, so tests and different environments can supply their own connection/model.


45-56: Autoscroll listener likely doesn’t need an extra Platform.runLater

model.getMessages() is mutated on the FX thread via FxUtils.runOnFx(...) in HelloModel, and ListView change listeners are invoked on the FX thread too. Wrapping scrollTo in an extra Platform.runLater adds another hop without clear benefit:

Platform.runLater(() -> {
    int size = messageView.getItems().size();
    if (size > 0) {
        messageView.scrollTo(size - 1);
    }
});

You can probably call scrollTo directly inside the listener and keep the logic simpler, unless you expect calls from non‑FX threads in the future.

src/test/java/com/example/NtfyConnectionSpy.java (1)

5-22: Avoid spawning a new thread per send in the test spy

NtfyConnectionSpy.send starts a brand new Thread on every call:

this.message = message;
new Thread(() -> callback.accept(true)).start();

For a test‐only spy this is usually more concurrency than needed and can make tests a bit slower or harder to reason about. Since the production implementation is already async, the tests would still be valid if the spy invoked the callback synchronously or via a lightweight executor.

Consider simplifying to a direct call (or a shared executor) unless you specifically want to test behavior under an extra thread hop.

src/test/java/com/example/HelloModelTest.java (1)

204-298: Replace Thread.sleep(100) in receive tests with deterministic synchronization

The receive‑side tests (shouldAddIncomingMessageToList, shouldDiscardNullIncomingMessage, shouldIgnoreMessagesWithBlankContent, shouldRejectAllInvalidIncomingMessages) all use Thread.sleep(100) before calling connectionSpy.simulateIncoming(...):

// give the listener time to attach
Thread.sleep(100);

Given the current implementation, handler registration (connection.receive(...)) and listener attachment are synchronous, so the sleeps are not strictly needed and can introduce flakiness or slow the test suite.

Consider removing these sleeps or replacing them with a deterministic signal (e.g. a latch that the spy or model counts down once receive is registered), so tests don’t depend on arbitrary timing.

src/main/java/com/example/HelloModel.java (1)

37-54: Consider guarding connection.send against synchronous exceptions

sendMessageAsync validates the message and then calls connection.send directly:

connection.send(msg, success -> {
    if (success) {
        runOnFx(() -> {
            if (msg.equals(messageToSend.get())) {
                messageToSend.set("");
            }
        });
    }
    callback.accept(success);
});

If a future NtfyConnection implementation throws synchronously from send, that exception will currently bubble up to the caller (e.g. the controller) and skip callback. You already test one such case using a throwing connection.

To make the model more robust, you could wrap the connection.send call in a try/catch, treating unexpected exceptions as success = false and still invoking the callback:

-        connection.send(msg, success -> {
-            if (success) {
-                runOnFx(() -> {
-                    if (msg.equals(messageToSend.get())) {
-                        messageToSend.set("");
-                    }
-                });
-            }
-            callback.accept(success);
-        });
+        try {
+            connection.send(msg, success -> {
+                if (success) {
+                    runOnFx(() -> {
+                        if (msg.equals(messageToSend.get())) {
+                            messageToSend.set("");
+                        }
+                    });
+                }
+                callback.accept(success);
+            });
+        } catch (Exception e) {
+            callback.accept(false);
+        }

This keeps UI code from having to catch transport‑layer exceptions.

src/main/java/com/example/NtfyConnectionImpl.java (1)

20-33: Tighten configuration error handling and response validation

Two small robustness points:

  1. Env configuration clarity
Dotenv dotenv = Dotenv.load();
hostName = Objects.requireNonNull(dotenv.get("HOST_NAME"));

If HOST_NAME is missing, this throws a NullPointerException without context. You can retain the strictness but improve debuggability:

-        hostName = Objects.requireNonNull(dotenv.get("HOST_NAME"));
+        hostName = Objects.requireNonNull(
+                dotenv.get("HOST_NAME"),
+                "HOST_NAME must be set in environment or .env"
+        );
  1. Check response status before parsing stream in receive

In receive, you currently parse response.body() regardless of HTTP status:

http.sendAsync(request, HttpResponse.BodyHandlers.ofLines())
    .thenAccept(response -> {
        try {
            response.body()
                .map(line -> { ... })
                .filter(Objects::nonNull)
                .forEach(messageHandler);
        } catch (Exception e) {
            System.err.println("Stream processing error: " + e.getMessage());
        }
    })

It’s safer to check for a successful status (e.g. 2xx) before treating the body as a stream of JSON messages and possibly log/ignore error responses instead of trying to parse them.

Also applies to: 56-85

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21c51e8 and ffa9303.

📒 Files selected for processing (16)
  • .gitignore (1 hunks)
  • pom.xml (3 hunks)
  • src/main/java/com/example/FxUtils.java (1 hunks)
  • src/main/java/com/example/HelloController.java (1 hunks)
  • src/main/java/com/example/HelloFX.java (1 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/main/java/com/example/ManyParameters.java (1 hunks)
  • src/main/java/com/example/ManyParametersBuilder.java (1 hunks)
  • src/main/java/com/example/NtfyConnection.java (1 hunks)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/main/java/com/example/NtfyMessageDto.java (1 hunks)
  • src/main/java/com/example/Singelton.java (1 hunks)
  • src/main/java/module-info.java (1 hunks)
  • src/main/resources/com/example/hello-view.fxml (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
  • src/test/java/com/example/NtfyConnectionSpy.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/HelloModel.java (1)
src/main/java/com/example/FxUtils.java (1)
  • FxUtils (5-19)
🔇 Additional comments (11)
.gitignore (1)

3-3: LGTM!

Excluding .env files is a security best practice, preventing accidental commits of environment variables and sensitive configuration data.

pom.xml (2)

74-74: LGTM!

The formatting adjustment to the JavaFX plugin option improves readability without changing functionality.


53-57: I need to verify whether Jackson 3.0.1 uses a different groupId than the 2.x versions:

Rewrite the Jackson dependency configuration—the current groupId is actually correct for Jackson 3.x.

Jackson 3.x uses groupId tools.jackson.core for the databind package, as documented in the official Jackson repository. In Jackson 3.x, groupIds now start with tools.jackson instead of com.fasterxml.jackson, with one exception: jackson-annotations remains under com.fasterxml.jackson.

The configuration in your pom.xml is correct. Imports from com.fasterxml.jackson.annotation are expected in Jackson 3.x, as annotations intentionally remain under the com.fasterxml.jackson groupId. This design ensures compatibility between Jackson 2.x and 3.x annotations despite the major version bump.

src/main/java/com/example/HelloFX.java (1)

15-15: LGTM!

Increasing the scene width from 640 to 1280 appropriately accommodates the expanded chat UI layout introduced in the updated FXML.

src/main/resources/com/example/hello-view.fxml (1)

7-28: LGTM!

The FXML restructuring introduces a well-organized chat interface:

  • VBox layout provides clear vertical composition
  • ListView with VBox.vgrow="ALWAYS" ensures messages fill available space
  • Input area properly combines TextArea with Send button
  • Padding and spacing enhance visual clarity

The UI components (messageView, messageInput, sendMessage action) align with the expanded controller and model functionality introduced in this PR.

src/main/java/com/example/NtfyConnection.java (1)

5-8: LGTM!

The interface design appropriately uses:

  • Asynchronous callback pattern for send to handle success/failure
  • Consumer pattern for receive to enable reactive message handling

Package-private visibility is suitable for internal messaging architecture.

src/main/java/com/example/NtfyMessageDto.java (1)

5-7: LGTM!

The record structure is clean and appropriate for a message DTO. Using @JsonIgnoreProperties(ignoreUnknown = true) is a best practice that provides forward compatibility when the message schema evolves.

src/main/java/com/example/HelloController.java (1)

32-40: Verify that msg.time() is epoch milliseconds before using Instant.ofEpochMilli

The cell factory formats timestamps with:

timeFormatter.format(Instant.ofEpochMilli(msg.time()))

This assumes NtfyMessageDto.time() is epoch milliseconds. If it is epoch seconds (or any other unit), the rendered times will be wrong even though the code compiles and runs.

Please double‑check the time field’s semantics and switch to Instant.ofEpochSecond(...) or apply appropriate scaling if needed.

src/test/java/com/example/HelloModelTest.java (1)

18-320: Strong and focused coverage around HelloModel behavior

The test suite covers successful sends, multiple sends, validation of blank/null messages, explicit failure/exception paths, incoming message filtering, and an integration path via WireMock. The use of CountDownLatch to guard async callbacks is solid and makes these tests robust.

src/main/java/com/example/HelloModel.java (1)

22-35: Good separation of concerns and FX‑thread safety in startReceiving

startReceiving delegates to connection.receive and uses isValidMessage plus runOnFx to filter and append messages:

connection.receive(incoming -> {
    if (!isValidMessage(incoming)) return;
    runOnFx(() -> messages.add(incoming));
});

This keeps the model agnostic of the concrete connection implementation while ensuring the observable list is only mutated on the FX thread, which is exactly what the UI bindings expect.

src/main/java/com/example/NtfyConnectionImpl.java (1)

3-19: The review comment is incorrect: the import is correct for Jackson 3.x

The project declares Jackson 3.0.1 (groupId: tools.jackson.core) in pom.xml. In Jackson 3.x, the package namespace is tools.jackson.databind, making the import import tools.jackson.databind.ObjectMapper; correct as written. The suggested fix to use com.fasterxml.jackson.databind.ObjectMapper would cause a compilation error, as that package name belongs to Jackson 2.x (which the project does not use).

Likely an incorrect or invalid review comment.

Comment on lines +7 to +18
public static void runOnFx(Runnable task) {
try {
if (Platform.isFxApplicationThread()) {
task.run();
} else {
Platform.runLater(task);
}
} catch (Exception e) {
//fallback for headless environments
task.run();
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Narrow the exception handling to avoid masking errors.

The current implementation catches all exceptions and falls back to direct execution, which could:

  • Hide legitimate errors (e.g., IllegalStateException, NullPointerException)
  • Cause race conditions if tasks are executed on the wrong thread
  • Make debugging difficult since exceptions are silently swallowed

Consider one of these approaches:

Option 1: Catch only the specific exception for headless environments

 public static void runOnFx(Runnable task) {
-    try {
-        if (Platform.isFxApplicationThread()) {
-            task.run();
-        } else {
-            Platform.runLater(task);
-        }
-    } catch (Exception e) {
-        //fallback for headless environments
+    if (Platform.isFxApplicationThread()) {
         task.run();
+    } else {
+        Platform.runLater(task);
     }
 }

Option 2: If headless support is truly needed, catch specific exception types

 public static void runOnFx(Runnable task) {
     try {
         if (Platform.isFxApplicationThread()) {
             task.run();
         } else {
             Platform.runLater(task);
         }
-    } catch (Exception e) {
-        //fallback for headless environments
+    } catch (IllegalStateException e) {
+        // FX platform not initialized (e.g., headless tests)
         task.run();
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static void runOnFx(Runnable task) {
try {
if (Platform.isFxApplicationThread()) {
task.run();
} else {
Platform.runLater(task);
}
} catch (Exception e) {
//fallback for headless environments
task.run();
}
}
public static void runOnFx(Runnable task) {
if (Platform.isFxApplicationThread()) {
task.run();
} else {
Platform.runLater(task);
}
}
Suggested change
public static void runOnFx(Runnable task) {
try {
if (Platform.isFxApplicationThread()) {
task.run();
} else {
Platform.runLater(task);
}
} catch (Exception e) {
//fallback for headless environments
task.run();
}
}
public static void runOnFx(Runnable task) {
try {
if (Platform.isFxApplicationThread()) {
task.run();
} else {
Platform.runLater(task);
}
} catch (IllegalStateException e) {
// FX platform not initialized (e.g., headless tests)
task.run();
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/FxUtils.java around lines 7 to 18, the broad
catch-all hides real errors and silently falls back; change the catch to only
handle the specific exceptions that indicate a non-JavaFX/headless environment
(e.g., IllegalStateException and java.awt.HeadlessException), log the caught
exception, perform the fallback task.run() only for those cases, and rethrow or
let other exceptions propagate so legitimate errors are not swallowed.

Comment on lines +3 to +8
public class ManyParameters {

public ManyParameters(String computerName, int timeout,
String method, int size, byte[] data) {

}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Constructor receives parameters but doesn't store them.

The constructor accepts five parameters but has an empty body with no field assignments. This means all data passed to the constructor is immediately lost. The class also has no fields declared to hold these values.

Add fields to the class and assign constructor parameters:

 package com.example;
 
 public class ManyParameters {
+    private final String computerName;
+    private final int timeout;
+    private final String method;
+    private final int size;
+    private final byte[] data;
 
     public ManyParameters(String computerName, int timeout,
                           String method, int size, byte[] data) {
-
+        this.computerName = computerName;
+        this.timeout = timeout;
+        this.method = method;
+        this.size = size;
+        this.data = data;
     }
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around lines 3 to 8, the
constructor accepts five parameters but the class declares no fields and the
constructor body is empty, so none of the provided values are stored; add
private fields for computerName (String), timeout (int), method (String), size
(int) and data (byte[]) to the class and assign each constructor parameter to
its corresponding field (this.field = parameter) inside the constructor,
ensuring defensive copying for the byte[] (e.g., this.data = parameter == null ?
null : parameter.clone()) if immutability is desired and add appropriate getters
if the values need to be accessed.

Comment on lines +11 to +18
static void main() {
ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Invalid main method signature.

The method signature static void main() won't be recognized as a valid Java entry point. Java requires public static void main(String[] args).

Apply this diff to fix the signature:

-    static void main() {
+    public static void main(String[] args) {
         ManyParametersBuilder builder = new ManyParametersBuilder();
         builder
                 .setComputerName("localhost")   //Fluent API
                 .setTimeout(10)
                 .setSize(0)
                 .createManyParameters();
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static void main() {
ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
}
public static void main(String[] args) {
ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
}
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around lines 11 to 18, the main
method uses an invalid signature `static void main()`; change it to the standard
Java entry point `public static void main(String[] args)` so the JVM can
recognize and run it, and keep the existing method body unchanged (invoke the
builder as before).

Comment thread src/main/java/com/example/NtfyConnection.java Outdated
@@ -0,0 +1,7 @@
package com.example;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Jackson import will fail due to dependency groupId mismatch.

This import statement expects Jackson from the standard com.fasterxml.jackson groupId, but pom.xml declares tools.jackson.core:jackson-databind. This inconsistency will cause compilation failure.

Resolution: After correcting the Jackson dependency in pom.xml (see pom.xml review comments), this import should work correctly.

🤖 Prompt for AI Agents
In src/main/java/com/example/NtfyMessageDto.java around line 3 the file imports
com.fasterxml.jackson.annotation.JsonIgnoreProperties but the pom currently
declares the Jackson artifact under the wrong groupId (tools.jackson.core),
causing a compilation error; fix by updating pom.xml to declare the
jackson-databind dependency with the correct groupId com.fasterxml.jackson.core
(artifactId jackson-databind and appropriate version), then refresh/rebuild the
project so the import resolves.

@@ -0,0 +1,14 @@
package com.example;

public class Singelton {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the typo in the class name.

The class name "Singelton" is misspelled. It should be "Singleton". This typo will propagate throughout the codebase if the class is used.

Apply this diff to correct the class name:

-public class Singelton {
+public class Singleton {

Also update the constructor and instance references:

-    private final static Singelton instance = new Singelton();
+    private final static Singleton instance = new Singleton();
     
-    private Singelton(){
+    private Singleton(){
         
     }
     
-    public static Singelton getInstance(){
+    public static Singleton getInstance(){
         return instance;
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/example/Singelton.java around line 3, the class name
"Singelton" is misspelled; rename the class to "Singleton", update the file name
to Singleton.java, rename the constructor to match the new class name, and
update any static instance variable and all references/usages across the
codebase to use "Singleton" instead of "Singelton" to keep identifiers
consistent and compile-time correct.

Comment on lines +3 to +14
public class Singelton {

private final static Singelton instance = new Singelton();

private Singelton(){

}

public static Singelton getInstance(){
return instance;
}
} No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the purpose and usage of this singleton.

The singleton class has no state, behavior, or methods beyond the pattern itself. Additionally, there's no evidence in the PR that this class is used anywhere in the codebase.

Please clarify:

  1. What is the intended purpose of this singleton in the messaging application?
  2. Is this class currently being used, or is it a placeholder for future implementation?

Run the following script to check for usage:


🏁 Script executed:

#!/bin/bash
# Search for references to Singelton in the codebase
rg -n --type java 'Singelton' -g '!Singelton.java'

Length of output: 0



---

🏁 Script executed:

```shell
#!/bin/bash
# Verify the singleton file exists
if [ -f "src/main/java/com/example/Singelton.java" ]; then
  echo "✓ File exists"
  echo "=== File content ==="
  cat "src/main/java/com/example/Singelton.java"
else
  echo "✗ File does not exist"
fi

echo ""
echo "=== Checking for Java files ==="
# Count Java files in the repo
java_count=$(fd -e java | wc -l)
echo "Total Java files: $java_count"
fd -e java | head -20

echo ""
echo "=== Searching for 'Singelton' (all occurrences including imports) ==="
rg -n 'Singelton' --type java

echo ""
echo "=== Searching for instantiation patterns ==="
rg -n 'new Singelton' --type java

echo ""
echo "=== Checking for import statements ==="
rg -n 'import.*Singelton' --type java

Length of output: 1475


Remove unused singleton class or implement its intended functionality.

Verification confirms this singleton is not used anywhere in the codebase—no other Java file imports or references it. The class currently has no state or behavior beyond the pattern itself, making it dead code.

Either remove src/main/java/com/example/Singelton.java entirely, or if it's intended for future use in the messaging application, implement its actual purpose and add documentation explaining its role.

🤖 Prompt for AI Agents
In src/main/java/com/example/Singelton.java lines 3-14: this singleton class is
unused and contains no state or behavior; either delete the file to remove dead
code or implement its intended purpose (rename to correct spelling "Singleton"
if appropriate), add meaningful state/methods for the messaging application,
include Javadoc explaining its role, add at least one unit test verifying
behavior, and update any imports/usages where it should be referenced; after
changes, run a project-wide search to ensure there are no leftover references
and run the build/tests to verify.

Comment thread src/main/java/module-info.java Outdated
Comment on lines +4 to +6
requires tools.jackson.databind;
requires io.github.cdimascio.dotenv.java;
requires java.net.http;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify module names align with correct dependency groupIds.

The requires tools.jackson.databind directive assumes the Jackson dependency uses the tools.jackson.core groupId, which appears incorrect (see pom.xml review comments). Once the Jackson dependency groupId is corrected in pom.xml, ensure the module name here matches the actual automatic module name or JPMS module name of the corrected dependency.


🏁 Script executed:

#!/bin/bash
# Locate and display pom.xml to check Jackson dependency configuration
find . -name "pom.xml" -type f | head -1 | xargs cat | grep -A 5 -B 5 -i "jackson"

Length of output: 453


🏁 Script executed:

#!/bin/bash
# Also check module-info.java to see full context
find . -name "module-info.java" -type f | head -1 | xargs cat

Length of output: 249


🏁 Script executed:

#!/bin/bash
# Search for any pom.xml files to ensure we find the right one
fd "pom.xml" --max-one-result

Length of output: 215


🌐 Web query:

tools.jackson.core jackson-databind JPMS module name

💡 Result:

The JPMS module name for tools.jackson.core:jackson-databind is

tools.jackson.databind. [1][2]

Sources:

  • jackson-databind README (shows 3.x Java package & module name). [1]
  • Maven Central artifact listing for tools.jackson.core:jackson-databind. [2]

🌐 Web query:

io.github.cdimascio dotenv-java JPMS module name

💡 Result:

Short answer: require the module name the library declares — if dotenv-java is a modular JAR it exposes a module name in its module-info (use that). If it is non‑modular, the automatic module name is either the Automatic-Module-Name entry in the JAR manifest (if present) or a name derived from the JAR file name. In practice check the published artifact:

  • Repo / README (shows the package is io.github.cdimascio.dotenv). (github.com)
  • Javadoc shows the packages are under io.github.cdimascio.dotenv — so likely module name to require will be either the explicit module name in module-info (if present) or an automatic module named from the JAR (often io.github.cdimascio.dotenv or io.github.cdimascio.dotenv-java). (javadoc.io)

How to confirm locally (pick one):

  1. Inspect the JAR manifest for Automatic-Module-Name: jar xf META-INF/MANIFEST.MF and look for Automatic-Module-Name.
  2. Ask the JAR for its module name: jar --describe-module --file .
  3. Or open the JAR and look for module-info.class at its root.

If you want, tell me which dotenv-java version/JAR you have and I’ll check its exact module name for you.

Citations:


🌐 Web query:

maven central io.github.cdimascio dotenv-java 3.2.0 automatic module name

💡 Result:

I couldn't find an Automatic-Module-Name entry published for io.github.cdimascio:dotenv-java:3.2.0. To verify locally, download the JAR and run:

  • unzip -p dotenv-java-3.2.0.jar META-INF/MANIFEST.MF

If Automatic-Module-Name is present you'll see it in that manifest; otherwise the JVM will derive an automatic module name from the filename (see how automatic module names are chosen). [1][2]

Sources:

  • Maven Central listing for dotenv-java (shows 3.2.0 artifact). [1]
  • Java automatic-module-name / automatic module docs (how MANIFEST.MF vs filename determines module name). [2]

🌐 Web query:

dotenv-java module-info.class declared module

💡 Result:

Short answer: If a dotenv-java JAR contains module-info.class it is an explicit (declared) module; if it does not (or only has Automatic-Module-Name in the manifest) it becomes an automatic module when placed on the module-path. That difference is why you can see “declared module” vs “automatic module” behavior and why modular resolution/duplicate-name errors occur. Use one of these fixes: put the library on the classpath (unnamed module), use a JAR that provides Automatic-Module-Name or an explicit module-info, repackage/remove module-info.class (shade/relocate), or keep all dependencies on the module-path and add proper requires/exports. (docs.oracle.com)

If you want, tell me the exact error you see or the dotenv-java artifact/version and your Java (JDK) version and I’ll recommend the minimal fix.

Citations:


🌐 Web query:

dotenv-java github cdimascio module-info.java

💡 Result:

The cdimascio/dotenv-java repository does not contain a module-info.java (i.e., it isn’t a named JPMS module). Source: repository file listing. [1]

If you need JPMS support, you can either:

  • treat the published JAR as an automatic module (add it to module-path and use requires <artifact‑id>), or
  • create your own module-info and require the library as an automatic module (or request/PR a module-info in the upstream repo). [1]

Sources:
[1] cdimascio/dotenv-java repository (files & README).


Fix incorrect module name for dotenv-java dependency.

The Jackson module name tools.jackson.databind is correct per the tools.jackson.core:jackson-databind dependency in pom.xml. However, the dotenv-java module name is incorrect. Since the dotenv-java library does not declare a module-info.java, it uses an automatic module name derived from its filename: dotenv.java.

Update line 5 in src/main/java/module-info.java:

requires dotenv.java;

instead of:

requires io.github.cdimascio.dotenv.java;
🤖 Prompt for AI Agents
In src/main/java/module-info.java around lines 4 to 6, the module declaration
uses the incorrect module name for the dotenv-java dependency; replace the
current requires io.github.cdimascio.dotenv.java; with requires dotenv.java; so
the module uses the automatic module name generated from the library jar
filename; update line 5 accordingly and save the file.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/main/java/module-info.java (1)

4-4: Incorrect module name for dotenv-java will cause module resolution failure.

The module name io.github.cdimascio.dotenv.java is incorrect. Since the dotenv-java library does not declare a module-info.java, it uses an automatic module name derived from its JAR filename: dotenv.java.

Apply this diff to fix the module name:

-    requires io.github.cdimascio.dotenv.java;
+    requires dotenv.java;
src/main/java/com/example/FxUtils.java (1)

7-16: Constrain the IllegalStateException catch to Platform calls, not the task itself

Catching IllegalStateException around the entire method means an IllegalStateException thrown from task.run() (on the FX thread) will be caught, the task will be run again inline, and the first failure is effectively masked. It’s safer to only guard the Platform calls and keep task execution outside the try/catch.

For example:

 static void runOnFx(Runnable task) {
-    try {
-        if (Platform.isFxApplicationThread()) task.run();
-        else Platform.runLater(task);
-    } catch (IllegalStateException notInitialized) {
-        task.run();
-    }
+    if (Platform.isFxApplicationThread()) {
+        task.run();
+        return;
+    }
+    try {
+        Platform.runLater(task);
+    } catch (IllegalStateException notInitialized) {
+        // FX platform not initialized (e.g. headless tests) – fallback
+        task.run();
+    }
 }

This keeps headless fallback behavior while avoiding double execution or swallowed exceptions from the task.

🧹 Nitpick comments (6)
src/main/java/com/example/HelloFX.java (1)

9-22: Scene and stylesheet setup looks good; note fail‑fast CSS loading

Using Objects.requireNonNull(HelloFX.class.getResource("style.css")) is a nice way to catch a missing stylesheet early; just be aware this will crash startup if the CSS isn’t on the classpath (e.g., mispackaged jar or some test setups). If you later want a softer failure mode, you could switch to a null‑check with logging instead of throwing.

src/main/resources/com/example/hello-view.fxml (1)

7-47: FXML/controller wiring is solid; minor branding mismatch

The fx:ids and onAction handlers line up with HelloController nicely, and the layout structure fits the new model. One small nit: the header text "Welcome to YadaChat!" doesn’t match the window title/greeting "RuneChat". Consider aligning these so the app has a consistent name.

src/main/resources/com/example/style.css (1)

1-163: JavaFX CSS is fine; static-analysis “unknown property” errors are tool noise

All the -fx-* properties, JavaFX pseudo-classes like :focused/:selected, and the dropshadow(...) effect are valid in JavaFX CSS, even though the web‑CSS linter (Biome) flags them as unknown. Functionally this stylesheet looks good for a JavaFX app.

If these lint errors are bothering you, consider:

  • Marking this file as JavaFX CSS / excluding it from web‑CSS rules, or
  • Adding an ignore configuration for -fx-* properties and these pseudo-classes/functions.
src/main/java/com/example/NtfyConnection.java (1)

13-23: No-op default setCurrentTopic can hide missing overrides

Right now setCurrentTopic(String topic) is a default no-op. Any NtfyConnection implementation that forgets to override it will still compile but won’t actually change topics, while callers (e.g. HelloModel) will assume the topic switched.

Consider either:

  • Dropping the default body so implementations are forced to override, or
  • Adding clear Javadoc that implementations are expected to persist and honor the topic, and only using the default in very limited/testing scenarios.
src/main/java/com/example/HelloModel.java (1)

67-92: Consider replacing System.out.println with a logger in the model

In canSendMessage/sendMessageAsync you print user‑visible issues ("Nothing to send!", "Failed to send message!") directly to stdout. For a GUI app this is easy for debugging, but in the longer term it’s usually more maintainable to use a logger (and let the controller surface user‑facing feedback via the UI).

This is non‑blocking and can be deferred.

src/main/java/com/example/HelloController.java (1)

21-25: Controller wiring works; consider injecting the model for testability

Creating the model with new HelloModel(new NtfyConnectionImpl()) directly in the controller ties this UI tightly to the concrete HTTP implementation and makes it harder to unit‑test the controller or swap in a different backend.

If you later want more flexibility, consider:

  • Injecting HelloModel via a setter or FXML controller factory, or
  • Providing a way to override the NtfyConnection (e.g., passing in a spy or mock in tests).

For now this is fine for a small app, but dependency injection here would make the controller easier to test and evolve.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffa9303 and 05dae89.

📒 Files selected for processing (11)
  • src/main/java/com/example/FxUtils.java (1 hunks)
  • src/main/java/com/example/HelloController.java (1 hunks)
  • src/main/java/com/example/HelloFX.java (1 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/main/java/com/example/NtfyConnection.java (1 hunks)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/main/java/module-info.java (1 hunks)
  • src/main/resources/com/example/hello-view.fxml (1 hunks)
  • src/main/resources/com/example/style.css (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
  • src/test/java/com/example/NtfyConnectionSpy.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/test/java/com/example/NtfyConnectionSpy.java
  • src/test/java/com/example/HelloModelTest.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/HelloModel.java (1)
src/main/java/com/example/FxUtils.java (1)
  • FxUtils (5-18)
🪛 Biome (2.1.2)
src/main/resources/com/example/style.css

[error] 3-3: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 4-4: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 5-5: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 6-6: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 11-11: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 12-12: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 13-13: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 18-18: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 19-19: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 20-20: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 25-25: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 26-26: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 27-27: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 28-28: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 29-29: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 33-33: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 34-34: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 38-38: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 39-39: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 40-40: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 41-41: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 42-42: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 43-43: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 44-44: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 47-47: Unexpected unknown pseudo-class focused

See MDN web docs for more details.

(lint/correctness/noUnknownPseudoClass)


[error] 48-48: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 49-49: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 53-53: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 54-54: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 55-55: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 56-56: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 57-57: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 58-58: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 59-59: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 63-63: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 67-67: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 72-72: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 73-73: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 74-74: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 75-75: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 76-76: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 80-80: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 81-81: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 84-84: Unexpected unknown pseudo-class selected

See MDN web docs for more details.

(lint/correctness/noUnknownPseudoClass)


[error] 85-85: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 90-90: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 91-91: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 92-92: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 92-92: Unexpected unknown function: dropshadow

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)


[error] 93-93: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 94-94: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 95-95: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 99-99: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 100-100: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 101-101: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 105-105: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 106-106: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 107-107: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 112-112: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 113-113: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 117-117: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 118-118: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 119-119: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 120-120: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 121-121: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 122-122: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 123-123: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 126-126: Unexpected unknown pseudo-class focused

See MDN web docs for more details.

(lint/correctness/noUnknownPseudoClass)


[error] 127-127: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 128-128: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 133-133: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 134-134: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 135-135: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 136-136: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 137-137: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 138-138: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 139-139: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 140-140: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 140-140: Unexpected unknown function: dropshadow

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)


[error] 144-144: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 148-148: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 152-152: Unexpected unknown pseudo-class vertical

See MDN web docs for more details.

(lint/correctness/noUnknownPseudoClass)


[error] 153-153: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 156-156: Unexpected unknown pseudo-class vertical

See MDN web docs for more details.

(lint/correctness/noUnknownPseudoClass)


[error] 157-157: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 158-158: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 161-161: Unexpected unknown pseudo-class vertical

See MDN web docs for more details.

(lint/correctness/noUnknownPseudoClass)


[error] 162-162: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)

🔇 Additional comments (2)
src/main/java/module-info.java (1)

5-7: LGTM! Module dependencies are correctly declared.

The module names for java.net.http, tools.jackson.databind, and javafx.graphics are all correct and align with the HTTP client functionality, JSON processing, and JavaFX UI requirements described in the PR summary.

src/main/java/com/example/NtfyConnectionImpl.java (1)

3-5: Verify the ObjectMapper import/package

The import tools.jackson.databind.ObjectMapper is unusual – the common Jackson class lives in com.fasterxml.jackson.databind.ObjectMapper. If you intended to use standard Jackson, this import/package likely won’t resolve and will cause compile errors; if you have a custom shaded or wrapper ObjectMapper under tools.jackson, that’s fine but worth double‑checking.

Please confirm that:

  • The dependency providing tools.jackson.databind.ObjectMapper is actually on the classpath/module-path, or
  • If this was meant to be Jackson, switch the import to com.fasterxml.jackson.databind.ObjectMapper (and matching module dependency).

Comment on lines +20 to +24
public HelloModel(NtfyConnection connection) {
this.connection = connection;
this.currentTopic.set(connection.getCurrentTopic());
receiveMessage();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Changing topic appears to create multiple long‑lived receive streams

HelloModel calls receiveMessage() in the constructor and again inside setCurrentTopic(). Each receiveMessage() delegates to NtfyConnection.receive(...), and in NtfyConnectionImpl that starts a new HttpClient.sendAsync(...BodyHandlers.ofLines()) stream that is never cancelled.

As a result, every topic change can leave the previous receive stream running indefinitely:

  • Extra HTTP connections/resources remain open.
  • You may end up processing messages from multiple topics concurrently (depending on server behavior).
  • There’s no way to shut down or replace the active stream from the model.

It would be safer to ensure only one active receive stream per model, e.g. by:

  • Extending NtfyConnection.receive to return a handle/future that can be cancelled when switching topic, or
  • Adding an explicit “stopReceive”/close method on the connection and calling it before starting a new receive.

Also applies to: 50-56, 95-100

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 20-24 (and also review
occurrences at 50-56 and 95-100), the class starts a new receive stream each
time the topic is set (constructor + setCurrentTopic) which leaves previous
HttpClient.sendAsync line-based streams running; change the logic so only one
active receive stream exists: have NtfyConnection.receive return a cancellable
handle (e.g., CompletableFuture or Closeable subscription) or add a
stopReceive/close method on NtfyConnection, store the current handle in
HelloModel, and when switching topics cancel/close the existing handle before
calling receive() for the new topic; also remove the redundant receive() call
from the constructor (or ensure it uses the same single-start path) and ensure
you cancel the active handle when HelloModel is closed/destroyed.

@coderabbitai

coderabbitai Bot commented Nov 14, 2025

Copy link
Copy Markdown

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #3

coderabbitai Bot added a commit that referenced this pull request Nov 14, 2025
Docstrings generation was requested by @Rickank.

* #2 (comment)

The following files were modified:

* `src/main/java/com/example/FxUtils.java`
* `src/main/java/com/example/HelloController.java`
* `src/main/java/com/example/HelloFX.java`
* `src/main/java/com/example/HelloModel.java`
* `src/main/java/com/example/NtfyConnection.java`
* `src/main/java/com/example/NtfyConnectionImpl.java`
* `src/test/java/com/example/NtfyConnectionSpy.java`

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
src/test/java/com/example/HelloModelTest.java (6)

16-24: Class-level setup and FX stub look fine, but scope WireMock and FX init deliberately

@WireMockTest on the whole class and a @BeforeAll that only logs are acceptable for now, but:

  • If only the integration test needs WireMock, consider moving @WireMockTest to a nested class or that single test to avoid starting the server for all tests.
  • Make sure HelloModel and collaborators don’t require a real JavaFX Platform (e.g., Platform.startup, Platform.runLater), otherwise initToolkit should actually initialize a headless toolkit instead of just printing.

26-44: Valid send test is solid; optionally assert callback success

The test correctly asserts that:

  • The async path completes within the timeout.
  • The spy sees the expected message.

You could also capture and assert the success flag in this test (mirroring later tests) to fully specify the contract for a successful send, but it’s not strictly required given other tests already cover it.


46-64: Replace Thread.sleep with callback-based assertion to avoid flakiness

Using Thread.sleep(100) after the latch introduces timing-based flakiness and slows tests unnecessarily. You can instead capture getMessageToSend() inside the callback and assert on that, removing the sleep entirely. For example:

-        CountDownLatch latch = new CountDownLatch(1);
-
-        // Act
-        model.sendMessageAsync(success -> latch.countDown());
-        boolean completed = latch.await(1, TimeUnit.SECONDS);
-
-        // Assert
-        assertThat(completed).isTrue();
-        Thread.sleep(100);
-        assertThat(model.getMessageToSend()).isEmpty();
+        CountDownLatch latch = new CountDownLatch(1);
+        final String[] messageAfterSend = new String[1];
+
+        // Act
+        model.sendMessageAsync(success -> {
+            messageAfterSend[0] = model.getMessageToSend();
+            latch.countDown();
+        });
+        boolean completed = latch.await(1, TimeUnit.SECONDS);
+
+        // Assert
+        assertThat(completed).isTrue();
+        assertThat(messageAfterSend[0]).isEmpty();

This keeps the test deterministic and purely event-driven.


148-178: Sequential send test may rely on ordering guarantees that aren’t explicit

The test assumes that after two sequential sendMessageAsync calls:

  • Both callbacks complete.
  • The last spy.message is "Second".

This is fine if HelloModel guarantees sends are serialized or that the last call wins deterministically. If sends are dispatched onto independent threads without ordering guarantees, concurrency scheduling could occasionally leave "First" as the last write, making the test flaky.

Consider either:

  • Ensuring HelloModel serializes send operations (e.g., via a single-thread executor), or
  • Adapting the spy to record all sent messages in order and asserting on the sequence rather than just the final value.

180-220: Clarify the expected contract when send throws

This test currently passes if either:

  • sendMessageAsync throws (then exceptionCaught[0] is true), or
  • No exception is thrown but the callback reports success == false.

That makes the contract for callers ambiguous. For UI code, it’s usually preferable that sendMessageAsync never throws and always report failures via the callback.

I recommend tightening the expectation to one behavior (ideally “no exception, callback with false”), e.g. by removing the try/catch and just asserting completed and result[0] == false. That will make both the code and tests clearer.


252-302: Null/blank receive tests give good negative coverage

The tests for null and blank/empty content ensure:

  • No additions are observed via the ListChangeListener.
  • model.getMessages() remains empty.

The latch-based “no addition within timeout” pattern is a practical way to test this behavior; just be aware that shorter timeouts reduce test duration but slightly increase the chance of spurious failure on very slow CI. The current 500 ms is usually acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3270b81 and 1d1a4b2.

📒 Files selected for processing (1)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
🔇 Additional comments (6)
src/test/java/com/example/HelloModelTest.java (6)

66-89: Empty-message validation test is clear and effective

This test nicely verifies that:

  • The async callback is invoked.
  • success is false.
  • The connection spy doesn’t receive a message for an empty string.

This is a good guard on preconditions for sending.


91-114: Null-message validation test complements the empty case well

The null-path test mirrors the empty-string test and confirms:

  • Callback is invoked with false.
  • No send occurs on the spy.

Together, these two tests clearly specify the “nothing to send” behavior.


116-146: Failure-path test correctly asserts message retention on failed send

The anonymous NtfyConnection that always calls callback.accept(false) is a clean way to simulate a failure, and the test correctly asserts:

  • Callback is called with false.
  • The original message remains in the model, so the user doesn’t lose unsent text.

Behavior and coverage look good here.


224-250: Positive receive test cleanly validates message propagation

This test nicely verifies that:

  • A simulated incoming NtfyMessageDto triggers a change event.
  • The message ends up in model.getMessages().

Using a ListChangeListener plus CountDownLatch is a reasonable pattern here.


304-338: Ordering test for multiple received messages is clear

This test verifies that three simulated messages:

  • All arrive within the timeout.
  • Are stored in model.getMessages() in the same order they’re delivered.

This is a strong, easy-to-read assertion of ordering semantics for incoming messages and complements the other receive tests well.


342-369: WireMock integration test is well-structured; just confirm NtfyConnectionImpl URL handling

The integration test cleanly wires:

  • A real NtfyConnectionImpl using wmRuntimeInfo.getHttpPort().
  • A stubbed POST /mytopic that returns 200.
  • Verification that a request to /mytopic with body "Hello World" was issued on success.

This is a solid end-to-end test. Just confirm that NtfyConnectionImpl indeed posts to {baseUrl}/mytopic (and not, say, another path or topic), so the test is aligned with the implementation and not accidentally passing due to different defaults.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/main/java/com/example/FxUtils.java (1)

14-17: Consider adding logging and documentation for the fallback path.

The fallback to direct execution is appropriate for headless test environments, but silently swallowing the exception could make debugging harder if this code is unexpectedly called in a production headless context.

Consider this improvement:

-        } catch (IllegalStateException notInitialized) {
-            //headless
+        } catch (IllegalStateException notInitialized) {
+            // JavaFX toolkit not initialized (headless test environment)
+            // Fall back to direct execution on the current thread
+            System.err.println("Warning: JavaFX platform not initialized, executing task directly");
             task.run();
         }

Alternatively, use a proper logger if available in the project for better control over log levels.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d1a4b2 and e323a3c.

📒 Files selected for processing (1)
  • src/main/java/com/example/FxUtils.java (1 hunks)
🔇 Additional comments (1)
src/main/java/com/example/FxUtils.java (1)

7-18: Good improvement! Previous feedback addressed.

The exception handling has been narrowed from catching all Exception types to specifically catching IllegalStateException, which is the correct exception thrown when the JavaFX toolkit is not initialized. This makes the fallback behavior predictable and avoids masking other errors.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/main/java/com/example/HelloModel.java (1)

56-63: Critical: Multiple receive streams cause resource leaks.

Each call to setCurrentTopic invokes receiveMessage(), which starts a new HTTP stream via connection.receive(). The previous stream is never cancelled, leading to:

  • Multiple open HTTP connections consuming resources
  • Potential processing of messages from old topics
  • No way to cleanly shut down active streams

Consider one of these solutions:

Option 1: Extend NtfyConnection.receive to return a cancellable handle (e.g., CompletableFuture or custom subscription object), store it in HelloModel, and cancel the previous handle before starting a new receive.

Option 2: Add a stopReceive() or close() method to NtfyConnection and call it before invoking receive() for the new topic.

Example for Option 2:

 public void setCurrentTopic(String topic) {
     if (topic != null && !topic.isBlank()) {
+        connection.stopReceive(); // Cancel existing stream
         connection.setCurrentTopic(topic);
         this.currentTopic.set(topic);
         messages.clear();
         receiveMessage();
     }
 }
🧹 Nitpick comments (3)
src/main/java/com/example/NtfyConnection.java (1)

3-4: Remove unused imports.

CompletableFuture and TimeUnit are imported but never used in this interface.

Apply this diff:

-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
src/main/java/com/example/HelloController.java (2)

24-24: Consider dependency injection for better testability.

Directly instantiating NtfyConnectionImpl in the controller creates tight coupling and makes unit testing difficult. Consider accepting the connection via constructor or factory.

For example:

private final HelloModel model;

public HelloController() {
    this(new HelloModel(new NtfyConnectionImpl()));
}

// Package-private constructor for testing
HelloController(HelloModel model) {
    this.model = model;
}

134-135: Combine Platform.runLater calls for efficiency.

Two consecutive Platform.runLater calls can be combined into a single one to reduce overhead.

Apply this diff:

-                Platform.runLater(() -> messageInput.clear());
-                Platform.runLater(() -> messageInput.requestFocus());
+                Platform.runLater(() -> {
+                    messageInput.clear();
+                    messageInput.requestFocus();
+                });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e323a3c and c37ddfa.

📒 Files selected for processing (9)
  • src/main/java/com/example/FxUtils.java (1 hunks)
  • src/main/java/com/example/HelloController.java (1 hunks)
  • src/main/java/com/example/HelloFX.java (1 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/main/java/com/example/NtfyConnection.java (1 hunks)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/main/java/com/example/NtfyMessageDto.java (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
  • src/test/java/com/example/NtfyConnectionSpy.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/com/example/HelloFX.java
  • src/main/java/com/example/NtfyMessageDto.java
  • src/test/java/com/example/HelloModelTest.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/HelloModel.java (1)
src/main/java/com/example/FxUtils.java (1)
  • FxUtils (5-26)
🔇 Additional comments (2)
src/main/java/com/example/FxUtils.java (1)

14-25: LGTM! Exception handling improved.

The exception handling has been narrowed to catch only IllegalStateException, which appropriately handles the case when JavaFX is not initialized (e.g., headless test environments) while allowing other exceptions to propagate naturally.

src/test/java/com/example/NtfyConnectionSpy.java (1)

8-30: LGTM! Test spy is appropriate for its purpose.

The test spy correctly implements the NtfyConnection interface with simplified behavior suitable for testing. The thread creation in send is acceptable for test scenarios where message volume is low.

setText(null);
setGraphic(null);
} else {
// Skapa bubble-label

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace Swedish comments with English.

Code comments should be in English to ensure accessibility for international collaborators and maintainers.

Apply this diff:

-                    // Skapa bubble-label
+                    // Create bubble label
                     Label bubble = new Label(msg.message());
-                    // Använd CSS-klasser för skickat/mottaget
+                    // Use CSS classes for sent/received messages
                     if (model.getUserId().equals(msg.id())) {
-        // Scrolla ner till senaste meddelandet
+        // Scroll down to the latest message
         model.getMessages().addListener((javafx.collections.ListChangeListener<NtfyMessageDto>) change -> {

Also applies to: 104-104, 120-120

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around lines 94, 104 and 120,
replace the Swedish comments with English equivalents: change the comment on
line 94 from "Skapa bubble-label" to "Create bubble label", and translate the
comments on lines 104 and 120 from Swedish to clear English (preserve intent and
punctuation), updating only the comment text so code behavior is unchanged.

public NtfyConnectionImpl() {
Dotenv dotenv = Dotenv.load();
this.hostName = Objects.requireNonNull(dotenv.get("HOST_NAME"));
this.userId = Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix incorrect Objects.requireNonNull usage.

The second parameter of Objects.requireNonNull is the exception message, not a default value. If USER_ID is not set in the environment, this will throw a NullPointerException with the message "USER_ID" instead of using "USER_ID" as a fallback.

Apply this diff to use the correct method for providing a default:

-        this.userId = Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID");
+        this.userId = dotenv.get("USER_ID", "USER_ID");

Or if you want to require the value (fail fast if not set):

-        this.userId = Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID");
+        this.userId = Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID must be set in environment");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.userId = Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID");
this.userId = Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID must be set in environment");
🤖 Prompt for AI Agents
In src/main/java/com/example/NtfyConnectionImpl.java around line 28, the call
this.userId = Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID");
incorrectly uses the second parameter as a fallback; replace it either with
Objects.requireNonNullElse(dotenv.get("USER_ID"), "<fallback-value>") to provide
a default USER_ID when missing, or keep the requireNonNull call but supply a
clear error message like Objects.requireNonNull(dotenv.get("USER_ID"), "USER_ID
must be set") to fail fast when the env var is absent.

Comment on lines +89 to +108
public void receive(Consumer<NtfyMessageDto> messageHandler) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.GET()
.uri(URI.create(hostName + "/" + currentTopic + "/json"))
.build();

http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines())
.thenAccept(response -> response.body()
.map(s -> {
try {
return mapper.readValue(s, NtfyMessageDto.class);
} catch (Exception e) {
System.out.println("Failed to parse message: " + e.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.peek(System.out::println)
.forEach(messageHandler));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Consider adding stream cancellation capability.

The receive method starts a long-lived HTTP stream but provides no mechanism to cancel it. This is the root cause of the resource leak when HelloModel.setCurrentTopic is called multiple times (each call starts a new stream without stopping the previous one).

Consider returning a cancellable handle or adding a stopReceive() method to the interface. For example:

private CompletableFuture<?> activeStream;

@Override
public void receive(Consumer<NtfyMessageDto> messageHandler) {
    if (activeStream != null) {
        activeStream.cancel(true);
    }
    
    HttpRequest httpRequest = HttpRequest.newBuilder()
            .GET()
            .uri(URI.create(hostName + "/" + currentTopic + "/json"))
            .build();

    activeStream = http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines())
            .thenAccept(response -> response.body()
                    // ... existing stream processing
            );
}

public void stopReceive() {
    if (activeStream != null) {
        activeStream.cancel(true);
        activeStream = null;
    }
}
🤖 Prompt for AI Agents
In src/main/java/com/example/NtfyConnectionImpl.java around lines 89 to 108, the
receive method starts a long-lived HTTP stream but never exposes any
cancellation handle, causing resource leaks when receive is called multiple
times; add a private CompletableFuture<?> activeStream field, on receive cancel
and null out any existing activeStream before starting a new one, assign the
HttpClient.sendAsync(...) result to activeStream, and provide either a
stopReceive() method that cancels and clears activeStream or change receive to
return the CompletableFuture so callers can cancel it themselves; ensure
cancellation uses activeStream.cancel(true) and that activeStream is set to null
after cancellation or when the stream completes exceptionally/normally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant