Skip to content
Closed

Lab3 #10

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
Empty file modified mvnw
100644 → 100755
Empty file.
16 changes: 16 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@
<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>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>4.0.0-beta.15</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
34 changes: 32 additions & 2 deletions src/main/java/com/example/HelloController.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
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;

/**
* 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());

public ListView<NtfyMessageDto> messageView;

@FXML
private Label messageLabel;

@FXML
private TextField inputField;

@FXML
private TextField topicField;

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

inputField.textProperty().bindBidirectional(model.messageToSendProperty());

topicField.textProperty().bindBidirectional(model.topicProperty());

messageView.setItems(model.getMessages());

model.connectToTopic();
}

public void sendMessage(ActionEvent actionEvent) {
if (!inputField.getText().trim().isEmpty()) {
model.sendMessage();
inputField.clear();
}
}

public void connectToTopic(ActionEvent actionEvent) {
model.connectToTopic();
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/com/example/HelloFX.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public class HelloFX extends Application {
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, 740, 480);
stage.setTitle("JavaFX Chat App \uD83D\uDCAC");
stage.setScene(scene);
stage.show();
}
Expand Down
58 changes: 58 additions & 0 deletions src/main/java/com/example/HelloModel.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,54 @@
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<NtfyMessageDto> messages = FXCollections.observableArrayList();
private final StringProperty messageToSend = new SimpleStringProperty();
private final StringProperty topic = new SimpleStringProperty("mytopic");

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

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

public StringProperty messageToSendProperty() {
return messageToSend;
}

public String getMessageToSend() {
return messageToSend.get();
}

public void setMessageToSend(String message) {
messageToSend.set(message);
}

public String getTopic() {
return topic.get();
}

public StringProperty topicProperty() {
return topic;
}

public void setTopic(String topic) {
this.topic.set(topic);
}

/**
* Returns a greeting based on the current Java and JavaFX versions.
*/
Expand All @@ -12,4 +57,17 @@ public String getGreeting() {
String javafxVersion = System.getProperty("javafx.version");
return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".";
}

public void sendMessage() {
connection.send(topic.get(), messageToSend.get());
}

public void connectToTopic() {
messages.clear();
receiveMessage();
}

public void receiveMessage() {
connection.receive(topic.get(), m -> Platform.runLater(() -> messages.add(m)));
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/example/NtfyConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example;

import java.util.function.Consumer;

public interface NtfyConnection {

public boolean send(String topic, String message);

public void receive(String topic, Consumer<NtfyMessageDto> messageHandler);

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

import io.github.cdimascio.dotenv.Dotenv;
import tools.jackson.databind.ObjectMapper;
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 invalid Jackson import for ObjectMapper.

tools.jackson.databind.ObjectMapper does not exist, so this class will not compile. Switch to the correct Jackson package.

-import tools.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
📝 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
import tools.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
🤖 Prompt for AI Agents
In src/main/java/com/example/NtfyConnectionImpl.java around line 4, the import
uses the non-existent package tools.jackson.databind.ObjectMapper; replace it
with the correct Jackson import com.fasterxml.jackson.databind.ObjectMapper so
the class compiles and uses the proper ObjectMapper implementation from the
Jackson library.


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.concurrent.CompletableFuture;
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();
private CompletableFuture<Void> receiveTask;

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 topic, String message) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString(message))
.header("Cache", "no")
.uri(URI.create(hostName + "/" + topic))
.build();
try {
var response = http.send(httpRequest, HttpResponse.BodyHandlers.discarding());
return true;
} catch (IOException e) {
System.out.println("Error sending message");
} catch (InterruptedException e) {
System.out.println("Interrupted sending message");
Thread.currentThread().interrupt();
}
return false;
}

@Override
public synchronized void receive(String topic, Consumer<NtfyMessageDto> messageHandler) {
if (receiveTask != null) {
receiveTask.cancel(true);
}
HttpRequest httpRequest = HttpRequest.newBuilder()
.GET()
.uri(URI.create(hostName + "/" + topic + "/json"))
.build();

receiveTask = http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines())
.thenAccept(response -> response.body()
.map(s -> {
try {
return mapper.readValue(s, NtfyMessageDto.class);
} catch (Exception e) {
System.err.println("Failed to parse message: " + e.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.filter(message -> "message".equals(message.event()))
.peek(System.out::println)
.forEach(messageHandler));
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/example/NtfyMessageDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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) {

@Override
public String toString() {
return message;
}

}
3 changes: 3 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
48 changes: 40 additions & 8 deletions src/main/resources/com/example/hello-view.fxml
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Label?>

<StackPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.HelloController">
<children>
<Label fx:id="messageLabel" text="Hello, JavaFX!" />
</children>
</StackPane>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox alignment="CENTER" spacing="15"
xmlns="http://javafx.com/javafx/17.0.12"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.HelloController">

<padding>
<Insets bottom="20" left="20" right="20" top="20" />
</padding>

<Label fx:id="messageLabel"
style="-fx-font-size: 20px;"
text="JavaFX Chat App 💬" />

<HBox alignment="CENTER_LEFT"
spacing="10">
<Label text="Topic:" />
<TextField fx:id="topicField"
text="mytopic"
HBox.hgrow="ALWAYS" />
<Button text="Connect"
onAction="#connectToTopic" />
</HBox>

<HBox spacing="10">
<TextField fx:id="inputField"
promptText="Type a message!"
HBox.hgrow="ALWAYS" />
<Button text="Send"
onAction="#sendMessage" />
</HBox>

<ListView fx:id="messageView"
VBox.vgrow="ALWAYS" />

</VBox>
Loading
Loading