-
Notifications
You must be signed in to change notification settings - Fork 66
Model #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Model #14
Changes from all commits
ba9d641
87e581f
906b4b2
404b1f1
16b1396
5de28bf
61b6033
bf7e2bf
8e888be
8f79299
64ef5aa
d5c2cd2
91d4a26
1cfcb6b
b665727
234a4e0
120da00
29fefae
4c8aaf9
a731882
e07b438
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| target/ | ||
| /.idea/ | ||
| .env |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,22 +1,71 @@ | ||||||||||||||||||||||||
| package com.example; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import javafx.application.Platform; | ||||||||||||||||||||||||
| import javafx.event.ActionEvent; | ||||||||||||||||||||||||
| import javafx.fxml.FXML; | ||||||||||||||||||||||||
| import javafx.scene.control.Label; | ||||||||||||||||||||||||
| import javafx.scene.control.*; | ||||||||||||||||||||||||
| import java.time.Instant; | ||||||||||||||||||||||||
| import java.time.ZoneId; | ||||||||||||||||||||||||
| import java.time.format.DateTimeFormatter; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * 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()); | ||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tight coupling makes testing difficult. The Apply this diff to enable dependency injection: + private HelloModel model;
+
+ public HelloController() {
+ this(new HelloModel(new NtfyConnectionImpl()));
+ }
+
+ // Package-private constructor for testing
+ HelloController(HelloModel model) {
+ this.model = model;
+ }
- private final HelloModel model = new HelloModel(new NtfyConnectionImpl());Alternatively, use a factory pattern or dependency injection framework. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @FXML | ||||||||||||||||||||||||
| private Label messageLabel; | ||||||||||||||||||||||||
| @FXML private Label messageLabel; | ||||||||||||||||||||||||
| @FXML private ListView<NtfyMessageDto> messageView; | ||||||||||||||||||||||||
| @FXML private TextArea messageInput; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private final DateTimeFormatter timeFormatter = | ||||||||||||||||||||||||
| DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.systemDefault()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @FXML | ||||||||||||||||||||||||
| private void initialize() { | ||||||||||||||||||||||||
| if (messageLabel != null) { | ||||||||||||||||||||||||
| messageLabel.setText(model.getGreeting()); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| //visa välkomstmeddelande | ||||||||||||||||||||||||
| messageLabel.setText(model.getGreeting()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| messageView.setItems(model.getMessages()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| messageInput.textProperty().bindBidirectional(model.messageToSendProperty()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| //formatera meddelanden i ListView | ||||||||||||||||||||||||
| messageView.setCellFactory(lv -> new ListCell<>() { | ||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| protected void updateItem(NtfyMessageDto msg, boolean empty) { | ||||||||||||||||||||||||
| super.updateItem(msg, empty); | ||||||||||||||||||||||||
| if (empty || msg == null) { | ||||||||||||||||||||||||
| setText(null); | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| setText("[" + timeFormatter.format(Instant.ofEpochMilli(msg.time())) + "] " + msg.message()); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Scrolla automatiskt till senaste meddelandet | ||||||||||||||||||||||||
| model.getMessages().addListener((javafx.collections.ListChangeListener<NtfyMessageDto>) change -> { | ||||||||||||||||||||||||
| while (change.next()) { | ||||||||||||||||||||||||
| if (change.wasAdded()) { | ||||||||||||||||||||||||
| Platform.runLater(() -> { | ||||||||||||||||||||||||
| int size = messageView.getItems().size(); | ||||||||||||||||||||||||
| if (size > 0) { | ||||||||||||||||||||||||
| messageView.scrollTo(size - 1); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @FXML | ||||||||||||||||||||||||
| private void sendMessage(ActionEvent event) { | ||||||||||||||||||||||||
| //skicka asynkront – HelloModel hanterar rensning och callback | ||||||||||||||||||||||||
| model.sendMessageAsync(success -> { | ||||||||||||||||||||||||
| if (!success) { | ||||||||||||||||||||||||
| Platform.runLater(() -> { | ||||||||||||||||||||||||
| Alert alert = new Alert(Alert.AlertType.ERROR, "Kunde inte skicka meddelandet."); | ||||||||||||||||||||||||
| alert.show(); | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,74 @@ | ||
| package com.example; | ||
|
|
||
| /** | ||
| * Model layer: encapsulates application data and business logic. | ||
| */ | ||
| import javafx.application.Platform; | ||
| import javafx.beans.property.SimpleStringProperty; | ||
| import javafx.beans.property.StringProperty; | ||
| import javafx.collections.FXCollections; | ||
| import javafx.collections.ObservableList; | ||
| import java.util.function.Consumer; | ||
|
|
||
| public class HelloModel { | ||
| /** | ||
| * Returns a greeting based on the current Java and JavaFX versions. | ||
| */ | ||
| public String getGreeting() { | ||
| String javaVersion = System.getProperty("java.version"); | ||
| String javafxVersion = System.getProperty("javafx.version"); | ||
| return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + "."; | ||
| } | ||
| } | ||
|
|
||
| private final NtfyConnection connection; | ||
| private final ObservableList<NtfyMessageDto> messages = FXCollections.observableArrayList(); | ||
| private final StringProperty messageToSend = new SimpleStringProperty(""); | ||
|
|
||
| public HelloModel(NtfyConnection connection) { | ||
| this.connection = connection; | ||
| startReceiving(); | ||
| } | ||
|
|
||
| private void startReceiving() { | ||
| connection.receive(incoming -> { | ||
| if (incoming == null || incoming.message() == null || incoming.message().isBlank()) { | ||
| return; | ||
| } | ||
| runOnFx(() -> messages.add(incoming)); | ||
| }); | ||
| } | ||
|
|
||
| public void sendMessageAsync(Consumer<Boolean> callback) { | ||
| String msg = messageToSend.get(); | ||
| if (msg == null || msg.isBlank()) { | ||
| callback.accept(false); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| connection.send(msg, success -> { | ||
| if (success) { | ||
| runOnFx(() -> { | ||
| if (msg.equals(messageToSend.get())) { | ||
| messageToSend.set(""); | ||
| } | ||
| }); | ||
| callback.accept(true); | ||
| } else { | ||
| callback.accept(false); | ||
| } | ||
| }); | ||
| } catch (Exception e) { | ||
| // FÅNGA ALLA EXCEPTIONS HÄR! | ||
| System.err.println("Exception during send: " + e.getMessage()); | ||
| callback.accept(false); | ||
| } | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private static void runOnFx(Runnable task) { | ||
| try { | ||
| if (Platform.isFxApplicationThread()) { | ||
| task.run(); | ||
| } else { | ||
| Platform.runLater(task); | ||
| } | ||
| } catch (Exception e) { | ||
| task.run(); // fallback i tester | ||
| } | ||
| } | ||
|
|
||
| public ObservableList<NtfyMessageDto> getMessages() { return messages; } | ||
| public String getMessageToSend() { return messageToSend.get(); } | ||
| public StringProperty messageToSendProperty() { return messageToSend; } | ||
| public void setMessageToSend(String v) { messageToSend.set(v); } | ||
| public String getGreeting() { return "Welcome to ChatApp"; } | ||
| } | ||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty constructor body - incomplete implementation. The constructor accepts five parameters but doesn't store or use them. Since the class declares no fields, constructed instances will have no state. This makes the builder pattern ineffective, as Add fields and assignments: 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 |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| static void main() { | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-standard main() signature. This method cannot serve as a Java application entry point because it's package-private and lacks the Apply this diff to fix the signature for a proper entry point: - static void main() {
+ public static void main(String[] args) {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| ManyParametersBuilder builder = new ManyParametersBuilder(); | ||||||||||||||||||||||||||
| builder | ||||||||||||||||||||||||||
| .setComputerName("localhost") //Fluent API | ||||||||||||||||||||||||||
| .setTimeout(10) | ||||||||||||||||||||||||||
| .setSize(0) | ||||||||||||||||||||||||||
| .createManyParameters(); | ||||||||||||||||||||||||||
|
Comment on lines
+12
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused object creation. The Apply this diff to capture the result: - ManyParametersBuilder builder = new ManyParametersBuilder();
- builder
+ ManyParameters params = new ManyParametersBuilder()
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
+ // Use params...📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| 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
+4
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent null initialization. Only Apply this diff for consistency: private String computerName;
private int timeout = 0;
private String method;
private int size = 0;
- private byte[] data = null;
+ private byte[] data;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 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; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public ManyParameters createManyParameters() { | ||||||||||||||||||||||
| return new ManyParameters(computerName, timeout, method, size, data); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.example; | ||
|
|
||
| import java.util.function.Consumer; | ||
|
|
||
| interface NtfyConnection { | ||
| void send(String message, Consumer<Boolean> callback); | ||
| void receive(Consumer<NtfyMessageDto> handler); | ||
| } | ||
|
|
||
| //public boolean send(String message); | ||
|
|
||
| //public void receive(Consumer<NtfyMessageDto> messageHandler); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,87 @@ | ||||||
| package com.example; | ||||||
|
|
||||||
| import io.github.cdimascio.dotenv.Dotenv; | ||||||
| import tools.jackson.databind.ObjectMapper; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix ObjectMapper import package
-import tools.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectMapper;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| import java.net.URI; | ||||||
| import java.net.http.HttpClient; | ||||||
| import java.net.http.HttpRequest; | ||||||
| import java.net.http.HttpResponse; | ||||||
| import java.time.Duration; | ||||||
| import java.util.Objects; | ||||||
| import java.util.function.Consumer; | ||||||
|
|
||||||
| public class NtfyConnectionImpl implements NtfyConnection { | ||||||
|
|
||||||
| private final HttpClient http; | ||||||
| private final String hostName; | ||||||
| private final ObjectMapper mapper = new ObjectMapper(); | ||||||
|
|
||||||
| public NtfyConnectionImpl() { | ||||||
| Dotenv dotenv = Dotenv.load(); | ||||||
| hostName = Objects.requireNonNull(dotenv.get("HOST_NAME")); | ||||||
| this.http = HttpClient.newBuilder() | ||||||
| .connectTimeout(Duration.ofSeconds(10)) | ||||||
| .build(); | ||||||
| } | ||||||
|
|
||||||
| public NtfyConnectionImpl(String hostName) { | ||||||
| this.hostName = hostName; | ||||||
| this.http = HttpClient.newBuilder() | ||||||
| .connectTimeout(Duration.ofSeconds(5)) | ||||||
| .build(); | ||||||
| } | ||||||
|
|
||||||
| @Override | ||||||
| public void send(String message, Consumer<Boolean> callback) { | ||||||
| HttpRequest request = HttpRequest.newBuilder() | ||||||
| .POST(HttpRequest.BodyPublishers.ofString(message)) | ||||||
| .uri(URI.create(hostName + "/mytopic")) | ||||||
| .header("Cache", "no") | ||||||
| .timeout(Duration.ofSeconds(10)) //request timeout | ||||||
| .build(); | ||||||
|
|
||||||
| http.sendAsync(request, HttpResponse.BodyHandlers.discarding()) | ||||||
| .thenAccept(response -> { | ||||||
| boolean success = response.statusCode() >= 200 && response.statusCode() < 300; | ||||||
| callback.accept(success); | ||||||
| }) | ||||||
| .exceptionally(throwable -> { | ||||||
| System.err.println("Error sending message: " + throwable.getMessage()); | ||||||
| callback.accept(false); | ||||||
| return null; | ||||||
| }); | ||||||
| } | ||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| @Override | ||||||
| public void receive(Consumer<NtfyMessageDto> messageHandler) { | ||||||
| HttpRequest request = HttpRequest.newBuilder() | ||||||
| .GET() | ||||||
| .uri(URI.create(hostName + "/mytopic/json")) | ||||||
| .timeout(Duration.ofSeconds(30)) //timeout för receive | ||||||
| .build(); | ||||||
|
|
||||||
| http.sendAsync(request, HttpResponse.BodyHandlers.ofLines()) | ||||||
| .thenAccept(response -> { | ||||||
| try { | ||||||
| response.body() | ||||||
| .map(line -> { | ||||||
| try { | ||||||
| return mapper.readValue(line, NtfyMessageDto.class); | ||||||
| } catch (Exception e) { | ||||||
| System.err.println("Failed to parse message: " + line); | ||||||
| return null; | ||||||
| } | ||||||
| }) | ||||||
| .filter(Objects::nonNull) | ||||||
| .forEach(messageHandler); | ||||||
| } catch (Exception e) { | ||||||
| System.err.println("Stream processing error: " + e.getMessage()); | ||||||
| } | ||||||
| }) | ||||||
| .exceptionally(ex -> { | ||||||
| System.err.println("Error receiving messages: " + ex.getMessage()); | ||||||
| return null; | ||||||
| }); | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Consider using a stable WireMock version.
The project uses WireMock version 4.0.0-beta.15, which is a beta release and may have stability or compatibility issues.
🌐 Web query:
💡 Result:
The latest stable WireMock release is 3.13.1 (released June 13, 2025). [1][2]
Sources:
[1] WireMock blog — "WireMock 3.13.1 Released" (June 13, 2025).
[2] GitHub release — wiremock/wiremock tag 3.13.1.
Use WireMock 3.13.1, the latest stable release.
The project currently uses WireMock 4.0.0-beta.15, a beta release that may have stability or compatibility issues. The latest stable version is 3.13.1, which should be preferred for production dependencies.
🤖 Prompt for AI Agents