From 388ee6fb8a1e623c016c1a38546e7ad9f56c8aa1 Mon Sep 17 00:00:00 2001 From: Simon Forsberg Date: Thu, 6 Nov 2025 09:45:17 +0100 Subject: [PATCH 1/6] Initial commit --- .gitignore | 1 + mvnw | 0 pom.xml | 16 +++++ .../java/com/example/HelloController.java | 12 +++- src/main/java/com/example/HelloModel.java | 41 ++++++++++++ src/main/java/com/example/NtfyConnection.java | 11 ++++ .../java/com/example/NtfyConnectionImpl.java | 65 +++++++++++++++++++ src/main/java/com/example/NtfyMessageDto.java | 7 ++ src/main/java/module-info.java | 3 + .../resources/com/example/hello-view.fxml | 13 ++-- src/test/java/com/example/HelloModelTest.java | 41 ++++++++++++ .../java/com/example/NtfyConnectionSpy.java | 19 ++++++ 12 files changed, 221 insertions(+), 8 deletions(-) mode change 100644 => 100755 mvnw create mode 100644 src/main/java/com/example/NtfyConnection.java create mode 100644 src/main/java/com/example/NtfyConnectionImpl.java create mode 100644 src/main/java/com/example/NtfyMessageDto.java create mode 100644 src/test/java/com/example/HelloModelTest.java create mode 100644 src/test/java/com/example/NtfyConnectionSpy.java diff --git a/.gitignore b/.gitignore index 6ac465db..5a54815d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ /.idea/ +.env \ No newline at end of file diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/pom.xml b/pom.xml index c40f667e..507a44b4 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,22 @@ javafx-fxml ${javafx.version} + + io.github.cdimascio + dotenv-java + 3.2.0 + + + tools.jackson.core + jackson-databind + 3.0.1 + + + org.wiremock + wiremock + 4.0.0-beta.15 + test + diff --git a/src/main/java/com/example/HelloController.java b/src/main/java/com/example/HelloController.java index fdd160a0..f83a10bf 100644 --- a/src/main/java/com/example/HelloController.java +++ b/src/main/java/com/example/HelloController.java @@ -1,14 +1,17 @@ package com.example; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Label; +import javafx.scene.control.ListView; /** * 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(null); + public ListView messageView; @FXML private Label messageLabel; @@ -18,5 +21,10 @@ private void initialize() { if (messageLabel != null) { messageLabel.setText(model.getGreeting()); } + messageView.setItems(model.getMessages()); } -} + + public void sendMessage(ActionEvent actionEvent) { + model.sendMessage(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/HelloModel.java b/src/main/java/com/example/HelloModel.java index 385cfd10..4628e899 100644 --- a/src/main/java/com/example/HelloModel.java +++ b/src/main/java/com/example/HelloModel.java @@ -1,9 +1,41 @@ package com.example; +import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + /** * Model layer: encapsulates application data and business logic. */ public class HelloModel { + + private final NtfyConnection connection; + private final ObservableList messages = FXCollections.observableArrayList(); + private final StringProperty messageToSend = new SimpleStringProperty(); + + public HelloModel(NtfyConnection connection) { + this.connection = connection; + receiveMessage(); + } + + public ObservableList getMessages() { + return messages; + } + + public String getMessageToSend() { + return messageToSend.get(); + } + + public StringProperty messageToSendProperty() { + return messageToSend; + } + + public void setMessageToSend(String message) { + messageToSend.set(message); + } + /** * Returns a greeting based on the current Java and JavaFX versions. */ @@ -12,4 +44,13 @@ public String getGreeting() { String javafxVersion = System.getProperty("javafx.version"); return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + "."; } + + public void sendMessage() { + connection.send(messageToSend.get()); + + } + + public void receiveMessage() { + connection.receive(m -> Platform.runLater(() -> messages.add(m))); + } } diff --git a/src/main/java/com/example/NtfyConnection.java b/src/main/java/com/example/NtfyConnection.java new file mode 100644 index 00000000..c83095dd --- /dev/null +++ b/src/main/java/com/example/NtfyConnection.java @@ -0,0 +1,11 @@ +package com.example; + +import java.util.function.Consumer; + +public interface NtfyConnection { + + public boolean send(String message); + + public void receive(Consumer messageHandler); + +} \ No newline at end of file diff --git a/src/main/java/com/example/NtfyConnectionImpl.java b/src/main/java/com/example/NtfyConnectionImpl.java new file mode 100644 index 00000000..0ce2f50a --- /dev/null +++ b/src/main/java/com/example/NtfyConnectionImpl.java @@ -0,0 +1,65 @@ +package com.example; + +import io.github.cdimascio.dotenv.Dotenv; +import tools.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Objects; +import java.util.function.Consumer; + +public class NtfyConnectionImpl implements NtfyConnection { + + private final HttpClient http = HttpClient.newHttpClient(); + private final String hostName; + private final ObjectMapper mapper = new ObjectMapper(); + + public NtfyConnectionImpl() { + Dotenv dotenv = Dotenv.load(); + hostName = Objects.requireNonNull(dotenv.get("HOST_NAME")); + } + + public NtfyConnectionImpl(String hostName) { + this.hostName = hostName; + } + + @Override + public boolean send(String message) { + HttpRequest httpRequest = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(message)) + .header("Cache", "no") + .uri(URI.create(hostName + "/mytopic")) + .build(); + try { + //Todo: handle long blocking send requests to not freeze the JavaFX thread + //1. Use thread send message? + //2. Use async? + var reponse = http.send(httpRequest, HttpResponse.BodyHandlers.discarding()); + return true; + } catch (IOException e) { + System.out.println("Error sending message"); + } catch (InterruptedException e) { + System.out.println("Interruped sending message"); + } + return false; + } + + @Override + public void receive(Consumer messageHandler) { + HttpRequest httpRequest = HttpRequest.newBuilder() + .GET() + .uri(URI.create(hostName + "/mytopic/json")) + .build(); + + http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()) + .thenAccept(response -> response.body() + .map(s -> + mapper.readValue(s, NtfyMessageDto.class)) + .filter(message -> message.event().equals("message")) + .peek(System.out::println) + .forEach(messageHandler)); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/NtfyMessageDto.java b/src/main/java/com/example/NtfyMessageDto.java new file mode 100644 index 00000000..697cccac --- /dev/null +++ b/src/main/java/com/example/NtfyMessageDto.java @@ -0,0 +1,7 @@ +package com.example; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record NtfyMessageDto(String id, long time, String event, String topic, String message) { +} \ No newline at end of file diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 71574a27..649be1ab 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,9 @@ module hellofx { requires javafx.controls; requires javafx.fxml; + requires io.github.cdimascio.dotenv.java; + requires java.net.http; + requires tools.jackson.databind; opens com.example to javafx.fxml; exports com.example; diff --git a/src/main/resources/com/example/hello-view.fxml b/src/main/resources/com/example/hello-view.fxml index 20a7dc82..21a868b9 100644 --- a/src/main/resources/com/example/hello-view.fxml +++ b/src/main/resources/com/example/hello-view.fxml @@ -1,9 +1,10 @@ - - - - + + + + - +