Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target/
/.idea/
.env
32 changes: 32 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,29 @@
<javafx.version>25</javafx.version>
</properties>
<dependencies>

<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>
Comment on lines +21 to +25
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

Incorrect groupId for Jackson dependency.

The groupId tools.jackson.core is incorrect. The correct groupId for Jackson is com.fasterxml.jackson.core.

Apply this diff to fix the dependency:

-        <dependency>
-            <groupId>tools.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-            <version>3.0.1</version>
-        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.18.2</version>
+        </dependency>

Note: Jackson 3.x is still in alpha/beta. Using the stable 2.x version (2.18.2 is the latest stable) is recommended unless you specifically need 3.x features.

📝 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
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.18.2</version>
</dependency>
🤖 Prompt for AI Agents
In pom.xml around lines 21 to 25, the Jackson dependency uses the wrong groupId
"tools.jackson.core" and an unstable 3.0.1 version; change the groupId to
"com.fasterxml.jackson.core" and update the version to a stable 2.x release (for
example 2.18.2) unless you intentionally need Jackson 3.x, ensuring the
dependency block references the correct groupId and recommended stable version.

<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>4.0.0-beta.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand All @@ -45,6 +62,12 @@
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand All @@ -63,6 +86,15 @@
<noManPages>true</noManPages>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>25</source>
<target>25</target>
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
</plugin>
Comment on lines +89 to +97
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

🧩 Analysis chain

Verify the need for preview features.

The compiler is configured to use Java 25 with --enable-preview. Preview features are experimental and may change in future releases. Ensure this is intentional and that the codebase actually uses preview features.


🏁 Script executed:

#!/bin/bash
# Search for common Java preview features in the codebase
echo "Checking for record patterns, pattern matching, or other preview features..."
rg -n "instanceof.*\(|record.*\{|sealed|permits" --type java

Length of output: 222


🏁 Script executed:

#!/bin/bash
# More comprehensive search for Java preview features
echo "=== Searching for switch pattern matching ==="
rg -n "switch.*{.*case.*->|case.*when" --type java -A 3

echo "=== Searching for sealed classes ==="
rg -n "sealed\s+class|sealed\s+interface" --type java

echo "=== Searching for text blocks ==="
rg -n '"""' --type java

echo "=== Searching for virtual threads or structured concurrency ==="
rg -n "Thread\.ofVirtual|ExecutorService|StructuredTaskScope" --type java

echo "=== Listing all .java files to get scope ==="
fd "\.java$" --type f | wc -l

Length of output: 502


🌐 Web query:

Java 25 preview features JEP

💡 Result:

Preview JEPs in JDK 25

  • JEP 470 — PEM Encodings of Cryptographic Objects (Preview). [1][2]
  • JEP 502 — Stable Values (Preview). [1][3]
  • JEP 505 — Structured Concurrency (Fifth Preview). [1][3]
  • JEP 507 — Primitive Types in patterns, instanceof, and switch (Third Preview). [1][3]

Other notable non-preview experimental/incubator JEPs in JDK 25:

  • JEP 508 — Vector API (Incubator). [1][3]
  • JEP 509 — JFR CPU-Time Profiling (Experimental). [1][3]

Sources: JDK 25 release notes and OpenJDK/Oracle announcements. [1][2][3]


Remove the --enable-preview flag from maven-compiler-plugin configuration.

Verification confirms the codebase does not use any Java 25 preview features. The only preview-related element found is a record class (NtfyMessageDto.java), but records are a standard feature since Java 16 and do not require preview enablement. Text blocks in tests are also standard since Java 15. No actual preview features (JEP 470, 502, 505, 507) are used. Remove <compilerArgs>--enable-preview</compilerArgs> from lines 89-97 in pom.xml.

🤖 Prompt for AI Agents
In pom.xml around lines 89 to 97 the maven-compiler-plugin is configured with a
compilerArgs element that enables Java preview features; remove the
<compilerArgs>--enable-preview</compilerArgs> entry from the plugin
configuration so the compiler no longer runs with preview enabled, leaving
source/target as-is.

</plugins>
</build>
</project>
138 changes: 132 additions & 6 deletions src/main/java/com/example/HelloController.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,148 @@
package com.example;

import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

/**
* Controller layer: mediates between the view (FXML) and the model.
* Handles updates of the chat-window.
*/
public class HelloController {

private final HelloModel model = new HelloModel();
//En model skapas som i bakgrunden är en lista och håller koll på meddelanden
private final HelloModel model = new HelloModel(new NtfyConnectionImpl());

//@FXML kopplingar
@FXML
private Label messageLabel;
private Button connectToServer;

@FXML
private Button disconnectFromServer;

//Kopplar ett textfält från FXML där användaren skriver ett meddelande
@FXML
private TextField messageInput;

//Kopplar en knapp från FXML som klickas på för att skicka meddelandet
@FXML
private Button sendButton;


//Ytan för alla meddelanden som visas
@FXML
private ListView<NtfyMessageDto> chatBox;

//Metoden körs automatiskt när appen startar
@FXML
private void initialize() {
if (messageLabel != null) {
messageLabel.setText(model.getGreeting());
//todo: Initialisera uppkopplingsknapparna för server-anslutning
//todo: metod för nätverksuppkoppling?

//Sätter ursprungstillståndet (default) för skicka-knappen

updateSendButtonState();

//Lägger till en lyssnare för att uppdatera knappen vid inmatning av text
messageInput.textProperty().addListener((observable, oldValue, newValue) -> updateSendButtonState());

//Om användaren trycker på Enter eller klickar med musen -> skicka meddelandet
messageInput.setOnAction((event) -> sendMessageToModel());
sendButton.setOnAction(event -> sendMessageToModel());

disconnectFromServer.setOnAction(event -> setDisconnectFromServer());
connectToServer.setOnAction(event -> setConnectToServer());

disconnectFromServer.setDisable(true);

//model.receiveMessage();
//Styr hur varje meddelande ska visas i chatboxen
chatBox.setCellFactory(listView -> new ListCell<>() {
@Override
protected void updateItem(NtfyMessageDto item, boolean empty) {
super.updateItem(item, empty);
//Kräver en null check då JavaFX återanvänder cellerna
if (item == null || empty) {
setText(null);
setGraphic(null);
}
else{
//Skapar en label med meddelande-texten och sätter en stil från css
Label label = new Label(item.message());
label.getStyleClass().add("message-bubble");

String time = item.formattedTime();
Label labelTime = new Label(time);
labelTime.getStyleClass().add("time-stamp");

//Layout
VBox messageBox = new VBox(label, labelTime);
messageBox.setSpacing(2);

//Vänster eller höger i ListView
HBox hbox = new HBox(messageBox);
hbox.setMaxWidth(chatBox.getWidth()-20);

String messagePosition = item.message();
if (messagePosition != null && messagePosition.startsWith("User:")) {
hbox.setAlignment(javafx.geometry.Pos.CENTER_RIGHT);
label.getStyleClass().add("outgoing-message");
} else {
hbox.setAlignment(javafx.geometry.Pos.CENTER_LEFT);
label.getStyleClass().add("incoming-message");
}
setGraphic(hbox);
}
}
});
//Kopplar Listan i view med ObservableList i HelloModel
chatBox.setItems(model.getMessages());
//Flyttar Platform.runlater till controller på grund av runtimeexeption när tester körs, även för en mer solid MVC
model.getMessages().addListener((ListChangeListener<NtfyMessageDto>) changes -> {
chatBox.refresh();
});
}

private void sendMessageToModel() {
String outgoingMessage = messageInput.getText().trim();
//Kontrollerar om text-fältet är tomt
if (!outgoingMessage.isEmpty()) {
model.sendMessage("User: " + outgoingMessage);
//tömmer sedan fältet där text matas in(prompt-meddelande visas igen)
messageInput.clear();
}
}
}

private void updateSendButtonState() {
// Kollar om texten, efter att ha tagit bort ledande/efterföljande mellanslag, är tom.
boolean isTextPresent = !messageInput.getText().trim().isEmpty();

//Nytt villkor för button för att ej kunna skicka meddelanden till servern om ej connectad
boolean isConnected = !disconnectFromServer.isDisabled();

// Sätt disable till TRUE om det INTE finns text.
sendButton.setDisable(!isTextPresent || !isConnected);
}

//Starta prenumeration via model och uppdaterar button
public void setConnectToServer() {
if(disconnectFromServer.isDisable()) {
model.receiveMessage();
connectToServer.setDisable(true);
disconnectFromServer.setDisable(false);
updateSendButtonState();
}
}

//Stoppar prenumerationen och uppdaterar button
public void setDisconnectFromServer() {
model.stopSubscription();
connectToServer.setDisable(false);
disconnectFromServer.setDisable(true);
updateSendButtonState();
}
}
9 changes: 7 additions & 2 deletions src/main/java/com/example/HelloFX.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package com.example;

import io.github.cdimascio.dotenv.Dotenv;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;

public class HelloFX extends Application {

@Override
public void start(Stage stage) throws Exception {

FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("hello-view.fxml"));
Parent root = fxmlLoader.load();
Scene scene = new Scene(root, 640, 480);
stage.setTitle("Hello MVC");
Scene scene = new Scene(root, 540, 650);
stage.setTitle("CatCode Messenger");
stage.getIcons().add(new Image(getClass().getResourceAsStream("/coolCat.png")));

stage.setScene(scene);
stage.show();
}
Expand Down
81 changes: 76 additions & 5 deletions src/main/java/com/example/HelloModel.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,86 @@
package com.example;

import io.github.cdimascio.dotenv.Dotenv;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.io.IOException;


/**
* Model layer: encapsulates application data and business logic.
*/
public class HelloModel {
/**
* Returns a greeting based on the current Java and JavaFX versions.
* Handles and returns a list of messages observed by JavaFX
* Stores, changes and returns data.
*/
public String getGreeting() {
String javaVersion = System.getProperty("java.version");
String javafxVersion = System.getProperty("javafx.version");
return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".";

//Lista som håller alla meddelanden
//FXCollections.observableArrayList() = Nyckel som gör listan ändrings-bar och uppdaterar GUIt
private final ObservableList<NtfyMessageDto> messages = FXCollections.observableArrayList();

//Kopplar upp till nätverket , används för att skicka och ta emot meddelanden
private final NtfyConnection connection;
//Innehåller meddelandet som ska skickas, kopplat till GUI via SimpleStringProperty
private final StringProperty messageToSend = new SimpleStringProperty();
//Fält för att kunna styra anslutningen
private Subscription subscription = null;

//Konstruktorn tar emot nätverkskoppling, antingen ett test via spy eller en riktig via impl
public HelloModel(NtfyConnection connection) {

this.connection = connection;
//subscription = receiveMessage(); //subscription startar automatiskt när modellen skapas
}

//getter från private, används av controller för att koppla til ListView
public ObservableList<NtfyMessageDto> getMessages() {
return messages;
}
//test
public String getMessageToSend() {
return messageToSend.get();
}
//Getter från private för meddelandet som ska skickas
public StringProperty messageToSendProperty() {
return messageToSend;
}
//Sätter meddelande för tester
public void setMessageToSend(String message) {
messageToSend.set(message);
}

//Sätter meddelandet till inkommande parameter från test, eller controller (connection skickar till nätverket)
public void sendMessage(String message) {

messageToSend.set(message);
connection.send(messageToSend.get());

}

//Startar en prenumeration på inkommande meddelnaden,
//Returnerar ett Subscription-objekt så den kan stoppas
public Subscription receiveMessage() {
if(subscription != null && subscription.isOpen()) {
return this.subscription;
}
return subscription = connection.receive(messages::add);


}

public void stopSubscription() {
if (subscription != null && subscription.isOpen())
try{
subscription.close();
} catch(IOException e) {
System.out.println("Error closing subscription" + e.getMessage());
}
}
Comment on lines +76 to 83
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix error message formatting.

Line 81 has a missing space in the error message concatenation, resulting in "Error closing subscription" instead of "Error closing subscription: ".

Apply this diff:

         if (subscription != null && subscription.isOpen())
             try{
             subscription.close();
         } catch(IOException e) {
-                System.out.println("Error closing subscription" + e.getMessage());
+                System.out.println("Error closing subscription: " + e.getMessage());
             }
     }
📝 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 stopSubscription() {
if (subscription != null && subscription.isOpen())
try{
subscription.close();
} catch(IOException e) {
System.out.println("Error closing subscription" + e.getMessage());
}
}
public void stopSubscription() {
if (subscription != null && subscription.isOpen())
try{
subscription.close();
} catch(IOException e) {
System.out.println("Error closing subscription: " + e.getMessage());
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 76 to 83, the error
message concatenation in the catch prints "Error closing subscription" and the
exception message without a separator; update the catch to print a colon and
space before the exception text (i.e., include ": " between the fixed message
and e.getMessage()) so the output reads "Error closing subscription: <message>";
ensure the rest of the try/catch stays intact.

}


17 changes: 17 additions & 0 deletions src/main/java/com/example/NtfyConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example;


import java.nio.file.Path;
import java.util.function.Consumer;

public interface NtfyConnection {

//Skicka ett meddelande till servern
boolean send(String message);

boolean sendFile(Path file, String messageWithFile);

//Startar en prenumeration och tar emot en consumer som ska köras varje gång ett meddelande kommer
Subscription receive(Consumer<NtfyMessageDto> consumer);

}
Loading
Loading