Skip to content
Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
target/
/.idea/
.env
package-lock.json
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>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.2.0</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
12 changes: 10 additions & 2 deletions src/main/java/com/example/HelloController.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
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(new NtfyConnectionImpl());
public ListView<NtfyMessageDto> messageView;

@FXML
private Label messageLabel;
Expand All @@ -18,5 +20,11 @@ private void initialize() {
if (messageLabel != null) {
messageLabel.setText(model.getGreeting());
}
messageView.setItems(model.getMessages());

}

public void sendMessage(ActionEvent actionEvent) {
model.sendMessage();
}
}
3 changes: 1 addition & 2 deletions src/main/java/com/example/HelloFX.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package com.example;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
Expand All @@ -13,7 +12,7 @@ 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");
stage.setTitle("Chatt Client");
stage.setScene(scene);
stage.show();
}
Expand Down
47 changes: 44 additions & 3 deletions src/main/java/com/example/HelloModel.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,56 @@
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();

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


public ObservableList<NtfyMessageDto> 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.
*/
public String getGreeting() {
String javaVersion = System.getProperty("java.version");
String javafxVersion = System.getProperty("javafx.version");
return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".";
return "Chat Client by Adam";
}

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

}

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

public class ManyParameters {

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

}

static void main(){
ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost") // Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
}
}
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;

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);
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/example/NtfyConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example;

import java.util.function.Consumer;

public interface NtfyConnection {
public boolean send(String message);

public void receive(Consumer<NtfyMessageDto> messageHandler);
}
62 changes: 62 additions & 0 deletions src/main/java/com/example/NtfyConnectionImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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();

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))
.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 response = http.send(httpRequest, HttpResponse.BodyHandlers.ofString());
return true;
} catch (IOException e) {
System.out.println("Error sending message");
} catch (InterruptedException e) {
System.out.println("Interrupted sending message");
}
return false;
}

@Override
public void receive(Consumer<NtfyMessageDto> messageHandler) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.GET()
.uri(URI.create(hostName + "/mytopic/json?since=wBuD2KGEaAe0"))
.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));
}
}
6 changes: 6 additions & 0 deletions src/main/java/com/example/NtfyMessageDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
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) {}
13 changes: 13 additions & 0 deletions src/main/java/com/example/Singelton.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example;

public class Singelton {
private final static Singelton instance = new Singelton();

private Singelton() {

}

public static Singelton getInstance() {
return instance;
}
}
4 changes: 4 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
module hellofx {
requires javafx.controls;
requires javafx.fxml;
requires io.github.cdimascio.dotenv.java;
requires java.net.http;
requires tools.jackson.databind;
requires javafx.graphics;

opens com.example to javafx.fxml;
exports com.example;
Expand Down
19 changes: 11 additions & 8 deletions src/main/resources/com/example/hello-view.fxml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<?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.scene.control.*?>
<?import javafx.scene.layout.*?>

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

<Label fx:id="messageLabel" text="Adams Chat Client" />
<Button onAction="#sendMessage" prefHeight="25.0" prefWidth="115.0" text="Send message" />
<ListView fx:id="messageView" />

</VBox>
45 changes: 45 additions & 0 deletions src/test/java/com/example/HelloModelTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.example;

import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.assertj.core.api.Assertions.assertThat;

@WireMockTest
class HelloModelTest {

@Test
@DisplayName("Given a model with messageToSend when calling sendMessage then send method on connection should be called ")
void sendMessageCallsConnectionWithMessageToSend() {

// Arrange Given
var spy = new NtfyConnectionSpy();
var model = new HelloModel(spy);
model.setMessageToSend("Hello World");

// Act When
model.sendMessage();

// Assert Then
assertThat(spy.message).isEqualTo("Hello World");
}

@Test
void sendMessageToFakeServer(WireMockRuntimeInfo wmRuntimeInfo) {
var con = new NtfyConnectionImpl("http://localhost:" + wmRuntimeInfo.getHttpPort());
var model = new HelloModel(con);
model.setMessageToSend("Hello World");
stubFor(post("/mytopic").willReturn(ok()));

model.sendMessage();

// Verify call made to server
verify(postRequestedFor(urlEqualTo("/mytopic"))
.withRequestBody(matching("Hello World")));
}

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

import java.util.function.Consumer;

public class NtfyConnectionSpy implements NtfyConnection {

String message;
@Override
public boolean send(String message) {
this.message = message;
return true;
}

@Override
public void receive(Consumer<NtfyMessageDto> messageHandler) {

}
}
Loading