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
Empty file modified mvnw
100644 → 100755
Empty file.
35 changes: 33 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
Expand All @@ -15,7 +16,9 @@
<assertj.core.version>3.27.6</assertj.core.version>
<mockito.version>5.20.0</mockito.version>
<javafx.version>25</javafx.version>
<jackson.version>2.19.2</jackson.version>
</properties>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand All @@ -35,6 +38,12 @@
<version>${mockito.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>
Comment on lines +41 to +46
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

🧩 Analysis chain

Verify WireMock beta version is intentional.

WireMock 4.0.0-beta.15 is a beta release. While test dependencies are lower risk, verify whether a stable release is available and should be used instead. Beta versions may introduce breaking changes or lack stability guarantees.


🌐 Web query:

What is the latest stable version of WireMock?

💡 Result:

The latest stable WireMock release is 3.13.1 (released June 12, 2025). [1][2]

Sources:
[1] WireMock community announcement for 3.13.1.
[2] WireMock website release notes / changelog (3.x series).


Use WireMock stable version 3.13.1 or document justification for beta.

Verification confirms WireMock 3.13.1 is the latest stable release. Update to the stable version unless there's a specific reason to use 4.0.0-beta.15 (e.g., testing new major version features). Even for test dependencies, stable releases are preferred.

🤖 Prompt for AI Agents
In pom.xml around lines 41 to 46, the WireMock dependency is pinned to a beta
release (4.0.0-beta.15); update the dependency to the stable release
(org.wiremock:wiremock:3.13.1) unless you have a documented reason to rely on
the beta. Modify the version element to 3.13.1 and, if you intentionally need
beta features, add a short comment in the pom or project docs explaining which
beta-only features are required and why upgrading to stable is not possible yet.

<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
Expand All @@ -45,7 +54,28 @@
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
Expand All @@ -55,7 +85,7 @@
<configuration>
<mainClass>com.example.HelloFX</mainClass>
<options>
<option>--enable-native-access=javafx.graphics</option>
<option>--enable-native-access=javafx.graphics</option>
</options>
<launcher>javafx</launcher>
<stripDebug>true</stripDebug>
Expand All @@ -66,3 +96,4 @@
</plugins>
</build>
</project>

45 changes: 39 additions & 6 deletions src/main/java/com/example/HelloController.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,55 @@
package com.example;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;

import java.io.File;

/**
* Controller layer: mediates between the view (FXML) and the model.
*/
public class HelloController {

private final HelloModel model = new HelloModel();
private final HelloModel model = new HelloModel(new NtfyConnectionImpl());
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

NtfyConnectionImpl requires a base URL parameter.

The NtfyConnectionImpl is instantiated without arguments, but based on the test at line 33 in HelloModelTest.java, it requires a base URL parameter: new NtfyConnectionImpl(baseUrl). This will cause a compilation error.

Please provide the base URL, potentially reading it from environment variables using the dotenv library that was added to dependencies.

Apply this diff to fix:

+import io.github.cdimascio.dotenv.Dotenv;
+
 public class HelloController {
 
-    private final HelloModel model = new HelloModel(new NtfyConnectionImpl());
+    private final Dotenv dotenv = Dotenv.load();
+    private final HelloModel model = new HelloModel(
+        new NtfyConnectionImpl(dotenv.get("NTFY_BASE_URL", "https://ntfy.sh"))
+    );
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around line 14, the HelloModel
is being constructed with new NtfyConnectionImpl() but NtfyConnectionImpl
requires a base URL parameter; update the code to initialize dotenv
(Dotenv.load()) and read the base URL (e.g. Dotenv.load().get("NTFY_BASE_URL")),
provide that value to new NtfyConnectionImpl(baseUrl) when constructing
HelloModel, and supply a sensible fallback (or throw a clear error) if the
environment variable is missing.


@FXML
private Label messageLabel;

@FXML
private ListView<NtfyMessageDto> messageView;

@FXML
private TextField inputMessage;

@FXML
private void initialize() {
if (messageLabel != null) {
messageLabel.setText(model.getGreeting());
messageLabel.setText(model.getGreeting());


messageView.setItems(model.getMessages());
}

@FXML
private void sendMessage(ActionEvent event) {
String text = inputMessage.getText();


model.setMessageToSend(text);
model.sendMessage();


inputMessage.clear();
}

@FXML
private void attachFile(ActionEvent event) {
FileChooser chooser = new FileChooser();
File file = chooser.showOpenDialog(messageView.getScene().getWindow());
if (file != null) {
model.sendFile(file);
}
}
}


66 changes: 57 additions & 9 deletions src/main/java/com/example/HelloModel.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,63 @@
package com.example;

/**
* Model layer: encapsulates application data and business logic.
*/
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class HelloModel {
/**
* Returns a greeting based on the current Java and JavaFX versions.
*/

private final NtfyConnection connection;
private final ObservableList<NtfyMessageDto> messages = FXCollections.observableArrayList();

private String messageToSend = "";

public HelloModel(NtfyConnection connection) {
this.connection = connection;

messages.add(new NtfyMessageDto("init", 0, "message", "mytopic", "Initial message"));

receiveMessages();
}
Comment on lines +18 to +24
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

Don't call receiveMessages() in the constructor.

Invoking receiveMessages() before the constructor completes allows the this reference to escape. If connection.receive(...) starts an asynchronous operation or background thread, the callback may access a partially constructed HelloModel instance, leading to concurrency hazards.

Move the call outside the constructor:

 public HelloModel(NtfyConnection connection) {
     this.connection = connection;
     messages.add(new NtfyMessageDto("init", 0, "message", "mytopic", "Initial message"));
-    receiveMessages();
 }

Then, ensure the caller (e.g., HelloController) explicitly invokes receiveMessages() after constructing the model:

HelloModel model = new HelloModel(connection);
model.receiveMessages();
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 18 to 24, the
constructor calls receiveMessages(), causing the this reference to escape;
remove the receiveMessages() call from the constructor and document/expect that
callers (e.g., HelloController) will call model.receiveMessages() immediately
after constructing the HelloModel instance so that no background threads or
callbacks can observe a partially constructed object.


public ObservableList<NtfyMessageDto> getMessages() {
return messages;
}

public String getMessageToSend() {
return messageToSend;
}

public void setMessageToSend(String messageToSend) {
this.messageToSend = messageToSend;
}

public void sendMessage() {
if (messageToSend != null && !messageToSend.isBlank()) {
connection.send(messageToSend);
}
messageToSend = "";
}

public void sendFile(File file) {
try {
byte[] data = Files.readAllBytes(file.toPath());
connection.sendFile(file.getName(), data);
} catch (IOException e) {
e.printStackTrace();
}
}
Comment on lines +45 to +52
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 null check, handle large files, and use proper logging.

This method has several issues:

  1. Missing null check: No validation for the file parameter, risking NPE.
  2. Memory issue: Files.readAllBytes() loads the entire file into memory, which can cause OutOfMemoryError for large files.
  3. Poor error handling: printStackTrace() is an anti-pattern; use a logging framework instead.

Apply this diff:

 public void sendFile(File file) {
+    if (file == null || !file.exists() || !file.canRead()) {
+        // Log error or throw IllegalArgumentException
+        return;
+    }
     try {
         byte[] data = Files.readAllBytes(file.toPath());
         connection.sendFile(file.getName(), data);
     } catch (IOException e) {
-        e.printStackTrace();
+        // Use proper logging: logger.error("Failed to send file: " + file.getName(), e);
     }
 }

For large files, consider streaming the data instead of reading it all at once, if NtfyConnection.sendFile supports streaming APIs.

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

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 48 to 55, validate the
file parameter (null check, file.exists() and file.canRead()) and return/log an
error early if invalid; avoid Files.readAllBytes() for large files — if
NtfyConnection.sendFile has a streaming API use a buffered InputStream
(Files.newInputStream or FileInputStream wrapped in BufferedInputStream) and
stream chunks to the connection, otherwise check file.length() and refuse or
handle files above a safe threshold instead of loading into memory; replace
e.printStackTrace() with the application's logger (logger.error("message", e))
and ensure streams are closed in a try-with-resources block.


public void receiveMessages() {
connection.receive(m -> Platform.runLater(() -> messages.add(m)));
}

public String getGreeting() {
String javaVersion = System.getProperty("java.version");
String javafxVersion = System.getProperty("javafx.version");
return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".";
return "Hello, JavaFX!";
}
}


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

public class ManyParameters {

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

}
Comment on lines +5 to +8
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

Constructor ignores all parameters.

The constructor accepts five parameters but the body is empty - none of the values are stored. This makes the ManyParameters object useless as it cannot retain any data passed to it.

Add fields and store the parameters:

 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 != null ? data.clone() : null;
     }
+
+    // Add getters as needed
+    public String getComputerName() { return computerName; }
+    public int getTimeout() { return timeout; }
+    public String getMethod() { return method; }
+    public int getSize() { return size; }
+    public byte[] getData() { return data != null ? data.clone() : null; }
 }

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

🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around lines 5 to 8, the
constructor accepts parameters but its body is empty so none of the 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 the corresponding field, making sure to defensively copy the byte[]
(e.g., this.data = data == null ? null : data.clone()) rather than storing the
incoming reference directly.



static void main() {
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 main method signature.

The main() method has the wrong signature to serve as a Java entry point. It's missing the String[] parameter and public modifier.

Apply this diff to fix the signature:

-    static void main() {
+    public static void main(String[] args) {
         ManyParametersBuilder builder = new ManyParametersBuilder();
📝 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() {
public static void main(String[] args) {
ManyParametersBuilder builder = new ManyParametersBuilder();
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around line 11, the Java entry
point method currently reads "static void main()" which has the wrong signature;
change it to the standard public static void main(String[] args) so the JVM can
recognise and invoke it as the program entry point. Ensure the method is
declared public, remains static, returns void, and accepts a single parameter of
type String[] named args.

ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost")
.setTimeout(10)
.setSize(0)
.createManyParameters();
Comment on lines +12 to +17
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

Builder result is unused and incomplete.

The createManyParameters() result is not assigned or used, and the example doesn't demonstrate setMethod() despite it being available in the builder.

Apply this diff to complete the demonstration:

     public static void main(String[] args) {
-        ManyParametersBuilder builder = new ManyParametersBuilder();
-        builder
+        ManyParameters params = new ManyParametersBuilder()
                 .setComputerName("localhost")   //Fluent API
                 .setTimeout(10)
+                .setMethod("GET")
                 .setSize(0)
                 .createManyParameters();
+        // Use params as needed
     }
📝 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
ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
ManyParameters params = new ManyParametersBuilder()
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setMethod("GET")
.setSize(0)
.createManyParameters();
// Use params as needed
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around lines 12 to 17, the
builder invocation currently calls createManyParameters() but doesn't store or
use the returned object and omits using the available setMethod() call; update
the snippet to call setMethod(...) (e.g., "POST"), adjust any placeholder values
as needed (e.g., setSize to a non-zero value if appropriate), assign the result
of createManyParameters() to a variable like ManyParameters params, and use or
return that variable so the example demonstrates building and consuming the
object.

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

public class ManyParametersBuilder {
private String computerName;
private int timeout = 0;
private String method;
private int size = 0;
private byte[] data = null;
Comment on lines +3 to +8
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

Builder allows construction with uninitialized required fields.

The builder permits creating a ManyParameters instance without setting computerName or method, which will be null. This could lead to NullPointerException in downstream code that expects these values.

Consider either:

  1. Making these fields required in a constructor:
 public class ManyParametersBuilder {
     private String computerName;
     private int timeout = 0;
     private String method;
     private int size = 0;
     private byte[] data = null;
+
+    public ManyParametersBuilder(String computerName, String method) {
+        this.computerName = computerName;
+        this.method = method;
+    }
  1. Or validate in createManyParameters():
public ManyParameters createManyParameters() {
    if (computerName == null || method == null) {
        throw new IllegalStateException("computerName and method are required");
    }
    return new ManyParameters(computerName, timeout, method, size, data);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParametersBuilder.java around lines 3 to 8, the
builder currently allows creating a ManyParameters with null computerName or
method; add a validation step in the builder's createManyParameters() (or
build()) method to check if computerName or method is null and throw an
IllegalStateException with a clear message like "computerName and method are
required" before constructing ManyParameters; alternatively, make those fields
required via the builder constructor or a static builder factory that takes
computerName and method so they cannot be omitted—pick one approach and
implement the corresponding change consistently.


public ManyParametersBuilder setComputerName(String computerName) {
this.computerName = computerName;
return this;
}

public ManyParametersBuilder setTimeout(int timeout) {
this.timeout = timeout;
return this;
}

public ManyParametersBuilder setMethod(String method) {
this.method = method;
return this;
}

public ManyParametersBuilder setSize(int size) {
this.size = size;
return this;
}

public ManyParametersBuilder setData(byte[] data) {
this.data = data;
return this;
}
Comment on lines +30 to +33
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

Missing defensive copy of byte array.

The data byte array is stored directly without a defensive copy, allowing external mutation after it's passed to the builder.

Apply this diff to add a defensive copy:

 public ManyParametersBuilder setData(byte[] data) {
-    this.data = data;
+    this.data = data != null ? data.clone() : null;
     return this;
 }
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParametersBuilder.java around lines 30 to 33,
the setData method assigns the incoming byte[] directly which allows external
mutation; change it to store a defensive copy (e.g., if data is null set
this.data = null else this.data = Arrays.copyOf(data, data.length)), add the
necessary import (java.util.Arrays), and ensure any getters or build() that
expose the byte[] also return a copy rather than the internal array.


public ManyParameters createManyParameters() {
return new ManyParameters(computerName, timeout, method, size, data);
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/example/NtfyConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example;

import java.util.function.Consumer;

public interface NtfyConnection {

/**
* Skickar ett textmeddelande till servern.
*
* @param message meddelandet som ska skickas
* @return true om det lyckades, false annars
*/
boolean send(String message);

/**
* Registrerar en mottagare som hanterar inkommande meddelanden.
*
* @param messageHandler funktion som hanterar inkommande NtfyMessageDto
*/
void receive(Consumer<NtfyMessageDto> messageHandler);

/**
* Skickar en fil till servern.
*
* @param filename namnet på filen
* @param data innehållet i filen som byte-array
*/
void sendFile(String filename, byte[] data);
}




Loading
Loading