Skip to content

Commit ffa9303

Browse files
committed
refactoring, tog bort try-catch i Hellomodel för fortsatt felsökning
1 parent 61b6033 commit ffa9303

7 files changed

Lines changed: 212 additions & 130 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.example;
2+
3+
import javafx.application.Platform;
4+
5+
public class FxUtils {
6+
7+
public static void runOnFx(Runnable task) {
8+
try {
9+
if (Platform.isFxApplicationThread()) {
10+
task.run();
11+
} else {
12+
Platform.runLater(task);
13+
}
14+
} catch (Exception e) {
15+
//fallback for headless environments
16+
task.run();
17+
}
18+
}
19+
}

src/main/java/com/example/HelloController.java

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import javafx.event.ActionEvent;
55
import javafx.fxml.FXML;
66
import javafx.scene.control.*;
7-
import javafx.scene.control.ListCell;
8-
97
import java.time.Instant;
108
import java.time.ZoneId;
119
import java.time.format.DateTimeFormatter;
@@ -14,58 +12,60 @@ public class HelloController {
1412

1513
private final HelloModel model = new HelloModel(new NtfyConnectionImpl());
1614

17-
@FXML
18-
private Label messageLabel;
15+
@FXML private Label messageLabel;
16+
@FXML private ListView<NtfyMessageDto> messageView;
17+
@FXML private TextArea messageInput;
1918

20-
@FXML
21-
private ListView<NtfyMessageDto> messageView;
22-
23-
@FXML
24-
private TextArea messageInput;
25-
26-
// Formatter för tid
2719
private final DateTimeFormatter timeFormatter =
28-
DateTimeFormatter.ofPattern("HH:mm:ss")
29-
.withZone(ZoneId.systemDefault());
20+
DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.systemDefault());
3021

3122
@FXML
3223
private void initialize() {
33-
// Visa hälsning
24+
//visa välkomstmeddelande
3425
messageLabel.setText(model.getGreeting());
3526

36-
// Koppla ListView till modellens meddelanden
3727
messageView.setItems(model.getMessages());
3828

39-
// Bind TextArea till modellens property
4029
messageInput.textProperty().bindBidirectional(model.messageToSendProperty());
4130

42-
// Snyggare visning av meddelanden
31+
//formatera meddelanden i ListView
4332
messageView.setCellFactory(lv -> new ListCell<>() {
4433
@Override
4534
protected void updateItem(NtfyMessageDto msg, boolean empty) {
4635
super.updateItem(msg, empty);
4736
if (empty || msg == null) {
4837
setText(null);
4938
} else {
50-
setText("[" + timeFormatter.format(Instant.ofEpochMilli(msg.time())) + "] "
51-
+ msg.message());
39+
setText("[" + timeFormatter.format(Instant.ofEpochMilli(msg.time())) + "] " + msg.message());
5240
}
5341
}
5442
});
5543

56-
// Scrolla automatiskt ner till senaste meddelandet
44+
// Scrolla automatiskt till senaste meddelandet
5745
model.getMessages().addListener((javafx.collections.ListChangeListener<NtfyMessageDto>) change -> {
58-
Platform.runLater(() -> {
59-
if (!messageView.getItems().isEmpty()) {
60-
messageView.scrollTo(messageView.getItems().size() - 1);
46+
while (change.next()) {
47+
if (change.wasAdded()) {
48+
Platform.runLater(() -> {
49+
int size = messageView.getItems().size();
50+
if (size > 0) {
51+
messageView.scrollTo(size - 1);
52+
}
53+
});
6154
}
62-
});
55+
}
6356
});
6457
}
6558

6659
@FXML
67-
private void sendMessage(ActionEvent actionEvent) {
68-
model.sendMessage();
69-
messageInput.clear();
60+
private void sendMessage(ActionEvent event) {
61+
//skicka asynkront – HelloModel hanterar rensning och callback
62+
model.sendMessageAsync(success -> {
63+
if (!success) {
64+
Platform.runLater(() -> {
65+
Alert alert = new Alert(Alert.AlertType.ERROR, "Kunde inte skicka meddelandet.");
66+
alert.show();
67+
});
68+
}
69+
});
7070
}
71-
}
71+
}
Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,56 @@
11
package com.example;
22

3-
import javafx.application.Platform;
43
import javafx.beans.property.SimpleStringProperty;
54
import javafx.beans.property.StringProperty;
65
import javafx.collections.FXCollections;
76
import javafx.collections.ObservableList;
87
import java.util.function.Consumer;
98

9+
import static com.example.FxUtils.runOnFx;
10+
1011
public class HelloModel {
1112

1213
private final NtfyConnection connection;
1314
private final ObservableList<NtfyMessageDto> messages = FXCollections.observableArrayList();
14-
private final StringProperty messageToSend = new SimpleStringProperty();
15+
private final StringProperty messageToSend = new SimpleStringProperty("");
1516

1617
public HelloModel(NtfyConnection connection) {
1718
this.connection = connection;
18-
receiveMessage();
19+
startReceiving();
20+
}
21+
22+
private void startReceiving() {
23+
connection.receive(incoming -> {
24+
if (!isValidMessage(incoming)) {
25+
return;
26+
}
27+
runOnFx(() -> messages.add(incoming));
28+
});
29+
}
30+
31+
private boolean isValidMessage(NtfyMessageDto message) {
32+
return message != null
33+
&& message.message() != null
34+
&& !message.message().isBlank();
35+
}
36+
37+
public void sendMessageAsync(Consumer<Boolean> callback) {
38+
String msg = messageToSend.get();
39+
if (msg == null || msg.isBlank()) {
40+
callback.accept(false);
41+
return;
42+
}
43+
44+
connection.send(msg, success -> {
45+
if (success) {
46+
runOnFx(() -> {
47+
if (msg.equals(messageToSend.get())) {
48+
messageToSend.set("");
49+
}
50+
});
51+
}
52+
callback.accept(success);
53+
});
1954
}
2055

2156
public ObservableList<NtfyMessageDto> getMessages() {
@@ -30,45 +65,11 @@ public StringProperty messageToSendProperty() {
3065
return messageToSend;
3166
}
3267

33-
public void setMessageToSend(String message) {
34-
messageToSend.set(message);
68+
public void setMessageToSend(String value) {
69+
messageToSend.set(value);
3570
}
3671

3772
public String getGreeting() {
38-
String javaVersion = System.getProperty("java.version");
39-
String javafxVersion = System.getProperty("javafx.version");
40-
return "Welcome to ChatApp, made in JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".";
41-
}
42-
43-
public void sendMessage() {
44-
String msg = messageToSend.get();
45-
if (msg == null || msg.isBlank()) {
46-
return;
47-
}
48-
connection.send(msg);
49-
}
50-
51-
public void sendMessageAsync(Consumer<Boolean> callback) {
52-
String msg = messageToSend.get();
53-
if (msg == null || msg.isBlank()) {
54-
callback.accept(false);
55-
return;
56-
}
57-
58-
try {
59-
boolean success = connection.send(msg);
60-
callback.accept(success);
61-
} catch (Exception e) {
62-
callback.accept(false);
63-
}
64-
}
65-
66-
private void receiveMessage() {
67-
connection.receive(message -> {
68-
if (message == null || message.message() == null || message.message().isBlank()) {
69-
return;
70-
}
71-
Platform.runLater(() -> messages.add(message));
72-
});
73+
return "Welcome to ChatApp";
7374
}
7475
}

src/main/java/com/example/NtfyConnection.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
import java.util.function.Consumer;
44

5-
public interface NtfyConnection {
5+
interface NtfyConnection {
6+
void send(String message, Consumer<Boolean> callback);
7+
void receive(Consumer<NtfyMessageDto> handler);
8+
}
69

7-
public boolean send(String message);
10+
//public boolean send(String message);
811

9-
public void receive(Consumer<NtfyMessageDto> messageHandler);
10-
11-
}
12+
//public void receive(Consumer<NtfyMessageDto> messageHandler);

src/main/java/com/example/NtfyConnectionImpl.java

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,60 +3,85 @@
33
import io.github.cdimascio.dotenv.Dotenv;
44
import tools.jackson.databind.ObjectMapper;
55

6-
import java.io.IOException;
76
import java.net.URI;
87
import java.net.http.HttpClient;
98
import java.net.http.HttpRequest;
109
import java.net.http.HttpResponse;
10+
import java.time.Duration;
1111
import java.util.Objects;
1212
import java.util.function.Consumer;
1313

1414
public class NtfyConnectionImpl implements NtfyConnection {
1515

16-
private final HttpClient http = HttpClient.newHttpClient();
16+
private final HttpClient http;
1717
private final String hostName;
1818
private final ObjectMapper mapper = new ObjectMapper();
1919

2020
public NtfyConnectionImpl() {
2121
Dotenv dotenv = Dotenv.load();
2222
hostName = Objects.requireNonNull(dotenv.get("HOST_NAME"));
23+
this.http = HttpClient.newBuilder()
24+
.connectTimeout(Duration.ofSeconds(10))
25+
.build();
2326
}
2427

2528
public NtfyConnectionImpl(String hostName) {
2629
this.hostName = hostName;
30+
this.http = HttpClient.newBuilder()
31+
.connectTimeout(Duration.ofSeconds(5))
32+
.build();
2733
}
2834

2935
@Override
30-
public boolean send(String message) {
31-
HttpRequest httpRequest = HttpRequest.newBuilder()
36+
public void send(String message, Consumer<Boolean> callback) {
37+
HttpRequest request = HttpRequest.newBuilder()
3238
.POST(HttpRequest.BodyPublishers.ofString(message))
33-
.header("Cache", "no")
3439
.uri(URI.create(hostName + "/mytopic"))
40+
.header("Cache", "no")
41+
.timeout(Duration.ofSeconds(10)) //request timeout
3542
.build();
36-
try {
37-
//Todo: handle long blocking send requests to not freeze the JavaFX thread
38-
//1. Use thread send message?
39-
//2. Use async?
40-
var reponse = http.send(httpRequest, HttpResponse.BodyHandlers.discarding());
41-
return true;
42-
} catch (IOException e) {
43-
System.out.println("Error sending message");
44-
} catch (InterruptedException e) {
45-
System.out.println("Interruped sending message");
46-
}
47-
return false;
43+
44+
http.sendAsync(request, HttpResponse.BodyHandlers.discarding())
45+
.thenAccept(response -> {
46+
boolean success = response.statusCode() >= 200 && response.statusCode() < 300;
47+
callback.accept(success);
48+
})
49+
.exceptionally(throwable -> {
50+
System.err.println("Error sending message: " + throwable.getMessage());
51+
callback.accept(false);
52+
return null;
53+
});
4854
}
4955

5056
@Override
5157
public void receive(Consumer<NtfyMessageDto> messageHandler) {
52-
HttpRequest httpRequest = HttpRequest.newBuilder()
58+
HttpRequest request = HttpRequest.newBuilder()
5359
.GET()
5460
.uri(URI.create(hostName + "/mytopic/json"))
61+
.timeout(Duration.ofSeconds(30)) //timeout för receive
5562
.build();
5663

57-
http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines())
58-
.thenAccept(response -> response.body()
59-
.map(s -> mapper.readValue(s, NtfyMessageDto.class))
60-
.forEach(messageHandler));
64+
http.sendAsync(request, HttpResponse.BodyHandlers.ofLines())
65+
.thenAccept(response -> {
66+
try {
67+
response.body()
68+
.map(line -> {
69+
try {
70+
return mapper.readValue(line, NtfyMessageDto.class);
71+
} catch (Exception e) {
72+
System.err.println("Failed to parse message: " + line);
73+
return null;
74+
}
75+
})
76+
.filter(Objects::nonNull)
77+
.forEach(messageHandler);
78+
} catch (Exception e) {
79+
System.err.println("Stream processing error: " + e.getMessage());
80+
}
81+
})
82+
.exceptionally(ex -> {
83+
System.err.println("Error receiving messages: " + ex.getMessage());
84+
return null;
85+
});
6186
}
6287
}

0 commit comments

Comments
 (0)