Skip to content

setup#27

Merged
AseelHamamreh merged 2 commits intomainfrom
first_branch
Nov 16, 2025
Merged

setup#27
AseelHamamreh merged 2 commits intomainfrom
first_branch

Conversation

@AseelHamamreh
Copy link
Contributor

@AseelHamamreh AseelHamamreh commented Nov 15, 2025

Summary by CodeRabbit

  • New Features

    • Added a chat interface with message list, input field and Send button.
    • Users can send messages and receive live incoming messages in the UI.
    • App window title updated to "Chat App" and launches the chat view on start.
  • Chores

    • Bumped Java compiler target to Java 25.

@coderabbitai
Copy link

coderabbitai bot commented Nov 15, 2025

Walkthrough

Adds a JavaFX chat feature: new ChatController and chat-view.fxml UI, a ChatModel implementing HTTP POST and SSE subscribe logic, switches the app to load the new FXML, adds java.net.http module requirement, and sets Maven compiler to Java 25.

Changes

Cohort / File(s) Summary
Build config
pom.xml
Configures maven-compiler-plugin to use Java 25 for source, target, and release.
Module descriptor
src/main/java/module-info.java
Adds requires java.net.http.
Application entry
src/main/java/com/example/HelloFX.java
Loads chat-view.fxml instead of hello-view.fxml and updates window title to "Chat App".
Chat UI controller & FXML
src/main/java/com/example/ChatController.java, src/main/resources/com/example/chat-view.fxml
Adds ChatController with @FXML ListView<String> messagesList, TextField inputField, initialize() subscribing to model, and onSend() that appends "Me: " + message, calls model.sendMessage(...), and clears input. Adds chat-view.fxml layout wired to the controller.
Chat model (networking & SSE)
src/main/java/com/example/ChatModel.java
Adds ChatModel with sendMessage(String) that posts JSON asynchronously and tracks locally-sent messages, and subscribe(Consumer<String>) that connects to an SSE endpoint, parses events, filters local messages, and dispatches received messages to the JavaFX thread. Includes regex parsing and basic error handling.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as ChatController\n(JavaFX)
    participant Model as ChatModel\n(HTTP POST / SSE)

    User->>UI: Click Send (onSend)
    UI->>UI: read & trim inputField
    alt non-empty
        UI->>UI: append "Me: " + message
        UI->>Model: sendMessage(message)
        Model->>Model: record message in sent set
        Model->>Model: async HTTP POST → sendUrl
        Note right of Model: async SSE subscribe reads events\nfilters local messages and parses payload
        Model->>UI: Platform.runLater -> deliver "Friend: " + msg
        UI->>UI: clear inputField
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect ChatModel: concurrent set handling, scheduling removal of sent message IDs, HTTP client usage, SSE stream parsing (regex), error handling, resource/stream closing.
  • Verify thread-safety and Platform.runLater usage in subscribe callback.
  • Confirm FXML fx:controller, fx:id bindings and that UI element types match controller fields.
  • Check module-info change’s compatibility with build/runtime and Maven compiler 25 setting.

Poem

🐰
I hop, I tap the Send button bright,
Messages tumble into the night,
From ListView burrows to network trail,
Java twenty-five sets wind in my tail,
Hooray—new chats take a joyful flight! 🎉🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'setup' is vague and generic, failing to convey meaningful information about the substantial changes in this pull request. Consider a more descriptive title that captures the main objective, such as 'Add chat application with JavaFX UI and SSE messaging' or 'Implement ChatController and ChatModel for real-time messaging'.
✅ 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

Tip

📝 Customizable high-level summaries are now available!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide custom instructions to shape the summary (bullet lists, tables, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example:

"Create a concise high-level summary as a bullet-point list. Then include a Markdown table showing lines added and removed by each contributing author."


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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pom.xml (1)

14-14: Update JUnit Jupiter version to a valid released version.

The specified version 6.0.0 does not exist. The latest released version of JUnit Jupiter is 5.13.4. Update junit.jupiter.version in pom.xml (line 14) to a valid version from the 5.x series, such as 5.13.4.

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

6-6: Reminder: Address the TODO comment.

The TODO indicates that the network functionality to send POST JSON to ntfy is not yet implemented.

Would you like me to help generate the HTTP client code for sending messages to ntfy, or should I open a new issue to track this task?

📜 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 48d7cb7.

📒 Files selected for processing (5)
  • pom.xml (1 hunks)
  • src/main/java/com/example/ChatController.java (1 hunks)
  • src/main/java/com/example/ChatModel.java (1 hunks)
  • src/main/java/com/example/HelloFX.java (1 hunks)
  • src/main/resources/com/example/chat-view.fxml (1 hunks)
🔇 Additional comments (4)
src/main/java/com/example/HelloFX.java (1)

12-19: LGTM!

The FXML loading logic is correct and properly integrated with the new chat-view.fxml.

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

1-24: LGTM!

The FXML layout is well-structured with proper bindings to the ChatController. The UI components (ListView, TextField, Button) are correctly configured with fx:id attributes that match the controller's @FXML fields.

pom.xml (2)

17-17: JavaFX 25 is available and verified as the latest stable release.

JavaFX 25 (latest patch 25.0.1) was released on October 21, 2025, confirming that the version specified in pom.xml is valid and current.


12-12: I need to verify Maven compiler plugin and JavaFX support for Java 25:

I need to verify the Maven compiler plugin version specified in the pom.xml to confirm it supports Java 25:

All Java 25 availability and support concerns are resolved.

Java 25 (JDK) was released on September 16, 2025 and is available for use. Maven compiler plugin version 3.14.1 supports Java 25, and JavaFX 25 is designed to work with JDK 25 and is recommended for use with the GA release of JDK 25. The pom.xml configuration is valid and supported.

@FXML
private TextField inputField;

private final ChatModel model = new ChatModel();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Prefer dependency injection over direct instantiation for better testability.

The direct instantiation of ChatModel creates tight coupling and makes the controller difficult to unit test.

Consider using constructor injection:

-private final ChatModel model = new ChatModel();
+private final ChatModel model;
+
+public ChatController() {
+    this(new ChatModel());
+}
+
+// Package-private constructor for testing
+ChatController(ChatModel model) {
+    this.model = model;
+}

This allows you to inject a mock ChatModel during testing.

🤖 Prompt for AI Agents
In src/main/java/com/example/ChatController.java around line 15, the controller
directly instantiates ChatModel which creates tight coupling and hinders unit
testing; change to constructor injection by removing the new ChatModel()
instantiation, add a constructor that accepts a ChatModel parameter and assigns
it to the final field, update any framework annotations if needed (e.g.,
@Autowired or leave as plain constructor for manual wiring), and update calling
code/tests to provide a mock or real ChatModel via the constructor.

Comment on lines +18 to +25
private void onSend() {
String message = inputField.getText().trim();
if (!message.isEmpty()) {
messagesList.getItems().add("Me: " + message);
model.sendMessage(message);
inputField.clear();
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for model.sendMessage().

The onSend() method should handle potential exceptions from model.sendMessage() to prevent the UI from becoming unresponsive.

Apply this diff to add error handling:

 @FXML
 private void onSend() {
     String message = inputField.getText().trim();
     if (!message.isEmpty()) {
-        messagesList.getItems().add("Me: " + message);
-        model.sendMessage(message);
-        inputField.clear();
+        try {
+            messagesList.getItems().add("Me: " + message);
+            model.sendMessage(message);
+            inputField.clear();
+        } catch (Exception e) {
+            System.err.println("Failed to send message: " + e.getMessage());
+            // TODO: Show error dialog to user
+        }
     }
 }
📝 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
private void onSend() {
String message = inputField.getText().trim();
if (!message.isEmpty()) {
messagesList.getItems().add("Me: " + message);
model.sendMessage(message);
inputField.clear();
}
}
private void onSend() {
String message = inputField.getText().trim();
if (!message.isEmpty()) {
try {
messagesList.getItems().add("Me: " + message);
model.sendMessage(message);
inputField.clear();
} catch (Exception e) {
System.err.println("Failed to send message: " + e.getMessage());
// TODO: Show error dialog to user
}
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/ChatController.java around lines 18-25, wrap the
call to model.sendMessage(...) with proper error handling and avoid blocking the
JavaFX thread: dispatch model.sendMessage(...) to a background thread (e.g.,
CompletableFuture.runAsync or a Task), catch any thrown exceptions, log them,
and then use Platform.runLater to update the UI (show an error Alert or mark the
message as failed) so the UI remains responsive; ensure inputField clearing and
messagesList updates occur on the JavaFX thread and adjust their order as needed
(e.g., show the sent message immediately, run sendMessage async, and on
exception notify the user).

Comment on lines +5 to +8
public void sendMessage(String message) {
// TODO: send POST JSON to ntfy
System.out.println("Sending message: " + message);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add input validation to prevent null or empty messages.

The method should validate the input parameter to avoid processing invalid messages.

Apply this diff to add validation:

 public void sendMessage(String message) {
+    if (message == null || message.isBlank()) {
+        throw new IllegalArgumentException("Message cannot be null or empty");
+    }
     // TODO: send POST JSON to ntfy
     System.out.println("Sending message: " + message);
 }
📝 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 void sendMessage(String message) {
// TODO: send POST JSON to ntfy
System.out.println("Sending message: " + message);
}
public void sendMessage(String message) {
if (message == null || message.isBlank()) {
throw new IllegalArgumentException("Message cannot be null or empty");
}
// TODO: send POST JSON to ntfy
System.out.println("Sending message: " + message);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/ChatModel.java around lines 5 to 8, the sendMessage
method currently does not validate its input; add a defensive check that the
message is not null and not blank (trim and verify length > 0) and if invalid
throw an IllegalArgumentException with a clear message (e.g., "message must not
be null or empty"); keep existing behavior for valid messages. Ensure the check
is the first statement in the method so invalid inputs are rejected before any
processing or I/O.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48d7cb7 and e2da7ee.

📒 Files selected for processing (5)
  • pom.xml (1 hunks)
  • src/main/java/com/example/ChatController.java (1 hunks)
  • src/main/java/com/example/ChatModel.java (1 hunks)
  • src/main/java/com/example/HelloFX.java (1 hunks)
  • src/main/java/module-info.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pom.xml
  • src/main/java/com/example/HelloFX.java

Comment on lines +44 to +50
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(sendUrl))
.header("Content-Type", "application/json")
.header("X-Client-ID", clientId)
.header("Title", "Friend: ")
.POST(HttpRequest.BodyPublishers.ofString(message))
.build();
Copy link

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 Content-Type/body mismatch to unblock sending.

We POST the raw message string but advertise Content-Type: application/json. The ntfy API rejects that combination with a 400/415 response, so nothing actually gets delivered even though the UI shows the message as sent. Align the header with the body (e.g., use text/plain; charset=UTF-8) or wrap the payload in proper JSON before posting.

Apply this diff to fix the header:

-                .header("Content-Type", "application/json")
+                .header("Content-Type", "text/plain; charset=UTF-8")
📝 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
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(sendUrl))
.header("Content-Type", "application/json")
.header("X-Client-ID", clientId)
.header("Title", "Friend: ")
.POST(HttpRequest.BodyPublishers.ofString(message))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(sendUrl))
.header("Content-Type", "text/plain; charset=UTF-8")
.header("X-Client-ID", clientId)
.header("Title", "Friend: ")
.POST(HttpRequest.BodyPublishers.ofString(message))
.build();
🤖 Prompt for AI Agents
In src/main/java/com/example/ChatModel.java around lines 44 to 50, the request
sets Content-Type: application/json while posting a raw message string; change
the Content-Type header to "text/plain; charset=UTF-8" (or alternatively wrap
the message in a proper JSON object) and ensure the request body is sent with
UTF-8 encoding so the ntfy API accepts the payload; update the header value and,
if applicable, use a UTF-8 string publisher or explicit encoding when building
the POST body.

Comment on lines +77 to +105
String raw = line.substring(5).trim();
String msg = parseMessage(raw);

if (msg != null && !sentMessages.contains(msg)) {
Platform.runLater(() -> onMessageReceived.accept(msg));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
})
.exceptionally(e -> { e.printStackTrace(); return null; });
}

private String parseMessage(String data) {
try {
Matcher eventMatcher = Pattern.compile("\"event\"\\s*:\\s*\"(.*?)\"").matcher(data);
if (eventMatcher.find()) {
String event = eventMatcher.group(1);

if (!"message".equals(event)) {
return null;
}
}

Matcher msgMatcher = Pattern.compile("\"message\"\\s*:\\s*\"(.*?)\"").matcher(data);
if (msgMatcher.find()) {
return msgMatcher.group(1).replace("\\\"", "\"");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't drop legitimate messages that match our recent text.

sentMessages.contains(msg) only compares the text, so any remote user who sends the same text within the five-second eviction window is silently filtered out. Because we already stamp each send with X-Client-ID, the SSE payload contains "client" for every event—compare that to clientId instead. That lets us remove the whole sentMessages bookkeeping and still suppress only our own echoes.

Apply this diff to switch to client-based filtering:

-                                String raw = line.substring(5).trim();
-                                String msg = parseMessage(raw);
-
-                                if (msg != null && !sentMessages.contains(msg)) {
-                                    Platform.runLater(() -> onMessageReceived.accept(msg));
-                                }
+                                String raw = line.substring(5).trim();
+                                MessagePayload payload = parseMessage(raw);
+
+                                if (payload != null && !clientId.equals(payload.client())) {
+                                    Platform.runLater(() -> onMessageReceived.accept(payload.message()));
+                                }

And adjust parseMessage to surface the client id:

-    private String parseMessage(String data) {
+    private MessagePayload parseMessage(String data) {
         try {
             Matcher eventMatcher = Pattern.compile("\"event\"\\s*:\\s*\"(.*?)\"").matcher(data);
             if (eventMatcher.find()) {
                 String event = eventMatcher.group(1);

                 if (!"message".equals(event)) {
                     return null;
                 }
             }

             Matcher msgMatcher = Pattern.compile("\"message\"\\s*:\\s*\"(.*?)\"").matcher(data);
             if (msgMatcher.find()) {
-                return msgMatcher.group(1).replace("\\\"", "\"");
+                String message = msgMatcher.group(1).replace("\\\"", "\"");
+                Matcher clientMatcher = Pattern.compile("\"client\"\\s*:\\s*\"(.*?)\"").matcher(data);
+                String client = clientMatcher.find() ? clientMatcher.group(1) : null;
+                return new MessagePayload(message, client);
             }
         } catch (Exception ignored) {}

         return null;
     }
+
+    private record MessagePayload(String message, String client) {}

Once this is in place you can drop the sentMessages set and its cleanup thread entirely—they are no longer needed.

@AseelHamamreh AseelHamamreh merged commit e2da7ee into ithsjava25:main Nov 16, 2025
1 of 2 checks passed
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