Initial commit

This commit is contained in:
Sebastian Proksch 2025-11-13 22:26:09 +01:00
commit e4c00f10e8
37 changed files with 2512 additions and 0 deletions

View file

@ -0,0 +1,57 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client;
import static com.google.inject.Guice.createInjector;
import java.io.IOException;
import java.net.URISyntaxException;
import com.google.inject.Injector;
import client.scenes.AddQuoteCtrl;
import client.scenes.MainCtrl;
import client.scenes.QuoteOverviewCtrl;
import client.utils.ServerUtils;
import javafx.application.Application;
import javafx.stage.Stage;
public class Main extends Application {
private static final Injector INJECTOR = createInjector(new MyModule());
private static final MyFXML FXML = new MyFXML(INJECTOR);
public static void main(String[] args) throws URISyntaxException, IOException {
launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
var serverUtils = INJECTOR.getInstance(ServerUtils.class);
if (!serverUtils.isServerAvailable()) {
var msg = "Server needs to be started before the client, but it does not seem to be available. Shutting down.";
System.err.println(msg);
return;
}
var overview = FXML.load(QuoteOverviewCtrl.class, "client", "scenes", "QuoteOverview.fxml");
var add = FXML.load(AddQuoteCtrl.class, "client", "scenes", "AddQuote.fxml");
var mainCtrl = INJECTOR.getInstance(MainCtrl.class);
mainCtrl.initialize(primaryStage, overview, add);
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import com.google.inject.Injector;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.util.Builder;
import javafx.util.BuilderFactory;
import javafx.util.Callback;
import javafx.util.Pair;
public class MyFXML {
private Injector injector;
public MyFXML(Injector injector) {
this.injector = injector;
}
public <T> Pair<T, Parent> load(Class<T> c, String... parts) {
try {
var loader = new FXMLLoader(getLocation(parts), null, null, new MyFactory(), StandardCharsets.UTF_8);
Parent parent = loader.load();
T ctrl = loader.getController();
return new Pair<>(ctrl, parent);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private URL getLocation(String... parts) {
var path = Path.of("", parts).toString();
return MyFXML.class.getClassLoader().getResource(path);
}
private class MyFactory implements BuilderFactory, Callback<Class<?>, Object> {
@Override
@SuppressWarnings("rawtypes")
public Builder<?> getBuilder(Class<?> type) {
return new Builder() {
@Override
public Object build() {
return injector.getInstance(type);
}
};
}
@Override
public Object call(Class<?> type) {
return injector.getInstance(type);
}
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client;
import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Scopes;
import client.scenes.AddQuoteCtrl;
import client.scenes.MainCtrl;
import client.scenes.QuoteOverviewCtrl;
public class MyModule implements Module {
@Override
public void configure(Binder binder) {
binder.bind(MainCtrl.class).in(Scopes.SINGLETON);
binder.bind(AddQuoteCtrl.class).in(Scopes.SINGLETON);
binder.bind(QuoteOverviewCtrl.class).in(Scopes.SINGLETON);
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client.scenes;
import com.google.inject.Inject;
import client.utils.ServerUtils;
import commons.Person;
import commons.Quote;
import jakarta.ws.rs.WebApplicationException;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.stage.Modality;
public class AddQuoteCtrl {
private final ServerUtils server;
private final MainCtrl mainCtrl;
@FXML
private TextField firstName;
@FXML
private TextField lastName;
@FXML
private TextField quote;
@Inject
public AddQuoteCtrl(ServerUtils server, MainCtrl mainCtrl) {
this.mainCtrl = mainCtrl;
this.server = server;
}
public void cancel() {
clearFields();
mainCtrl.showOverview();
}
public void ok() {
try {
server.addQuote(getQuote());
} catch (WebApplicationException e) {
var alert = new Alert(Alert.AlertType.ERROR);
alert.initModality(Modality.APPLICATION_MODAL);
alert.setContentText(e.getMessage());
alert.showAndWait();
return;
}
clearFields();
mainCtrl.showOverview();
}
private Quote getQuote() {
var p = new Person(firstName.getText(), lastName.getText());
var q = quote.getText();
return new Quote(p, q);
}
private void clearFields() {
firstName.clear();
lastName.clear();
quote.clear();
}
public void keyPressed(KeyEvent e) {
switch (e.getCode()) {
case ENTER:
ok();
break;
case ESCAPE:
cancel();
break;
default:
break;
}
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client.scenes;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Pair;
public class MainCtrl {
private Stage primaryStage;
private QuoteOverviewCtrl overviewCtrl;
private Scene overview;
private AddQuoteCtrl addCtrl;
private Scene add;
public void initialize(Stage primaryStage, Pair<QuoteOverviewCtrl, Parent> overview,
Pair<AddQuoteCtrl, Parent> add) {
this.primaryStage = primaryStage;
this.overviewCtrl = overview.getKey();
this.overview = new Scene(overview.getValue());
this.addCtrl = add.getKey();
this.add = new Scene(add.getValue());
showOverview();
primaryStage.show();
}
public void showOverview() {
primaryStage.setTitle("Quotes: Overview");
primaryStage.setScene(overview);
overviewCtrl.refresh();
}
public void showAdd() {
primaryStage.setTitle("Quotes: Adding Quote");
primaryStage.setScene(add);
add.setOnKeyPressed(e -> addCtrl.keyPressed(e));
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client.scenes;
import java.net.URL;
import java.util.ResourceBundle;
import com.google.inject.Inject;
import client.utils.ServerUtils;
import commons.Quote;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
public class QuoteOverviewCtrl implements Initializable {
private final ServerUtils server;
private final MainCtrl mainCtrl;
private ObservableList<Quote> data;
@FXML
private TableView<Quote> table;
@FXML
private TableColumn<Quote, String> colFirstName;
@FXML
private TableColumn<Quote, String> colLastName;
@FXML
private TableColumn<Quote, String> colQuote;
@Inject
public QuoteOverviewCtrl(ServerUtils server, MainCtrl mainCtrl) {
this.server = server;
this.mainCtrl = mainCtrl;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
colFirstName.setCellValueFactory(q -> new SimpleStringProperty(q.getValue().person.firstName));
colLastName.setCellValueFactory(q -> new SimpleStringProperty(q.getValue().person.lastName));
colQuote.setCellValueFactory(q -> new SimpleStringProperty(q.getValue().quote));
}
public void addQuote() {
mainCtrl.showAdd();
}
public void refresh() {
var quotes = server.getQuotes();
data = FXCollections.observableList(quotes);
table.setItems(data);
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client.utils;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import org.glassfish.jersey.client.ClientConfig;
import commons.Quote;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.GenericType;
public class ServerUtils {
private static final String SERVER = "http://localhost:8080/";
public void getQuotesTheHardWay() throws IOException, URISyntaxException {
var url = new URI("http://localhost:8080/api/quotes").toURL();
var is = url.openConnection().getInputStream();
var br = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
public List<Quote> getQuotes() {
return ClientBuilder.newClient(new ClientConfig()) //
.target(SERVER).path("api/quotes") //
.request(APPLICATION_JSON) //
.get(new GenericType<List<Quote>>() {});
}
public Quote addQuote(Quote quote) {
return ClientBuilder.newClient(new ClientConfig()) //
.target(SERVER).path("api/quotes") //
.request(APPLICATION_JSON) //
.post(Entity.entity(quote, APPLICATION_JSON), Quote.class);
}
public boolean isServerAvailable() {
try {
ClientBuilder.newClient(new ClientConfig()) //
.target(SERVER) //
.request(APPLICATION_JSON) //
.get();
} catch (ProcessingException e) {
if (e.getCause() instanceof ConnectException) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="146.0" prefWidth="470.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.AddQuoteCtrl">
<children>
<Button layoutX="419.0" layoutY="105.0" mnemonicParsing="false" onAction="#ok" text="Ok" />
<Button layoutX="350.0" layoutY="105.0" mnemonicParsing="false" onAction="#cancel" text="Cancel" />
<TextField fx:id="firstName" layoutX="109.0" layoutY="24.0" prefHeight="26.0" prefWidth="79.0" />
<TextField fx:id="lastName" layoutX="286.0" layoutY="24.0" />
<TextField fx:id="quote" layoutX="109.0" layoutY="61.0" prefHeight="26.0" prefWidth="345.0" />
<Label layoutX="216.0" layoutY="29.0" text="Last Name" />
<Label layoutX="28.0" layoutY="29.0" text="First Name" />
<Label layoutX="28.0" layoutY="66.0" text="Quote" />
</children>
</AnchorPane>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.QuoteOverviewCtrl">
<children>
<Button layoutX="503.0" layoutY="360.0" mnemonicParsing="false" onAction="#addQuote" text="Add Quote" />
<Button layoutX="431.0" layoutY="360.0" mnemonicParsing="false" onAction="#refresh" text="Refresh" />
<TableView fx:id="table" layoutX="14.0" layoutY="14.0" prefHeight="337.0" prefWidth="572.0">
<columns>
<TableColumn fx:id="colFirstName" prefWidth="113.0" text="First Name" />
<TableColumn fx:id="colLastName" prefWidth="114.0" text="Last Name" />
<TableColumn fx:id="colQuote" prefWidth="344.0" text="Quote" />
</columns>
</TableView>
</children>
</AnchorPane>