Merge remote-tracking branch 'origin/main'
@@ -6,11 +6,13 @@ stages:
|
|||||||
|
|
||||||
# Reusable definitions
|
# Reusable definitions
|
||||||
.gradle-cache: &gradle-cache
|
.gradle-cache: &gradle-cache
|
||||||
|
variables:
|
||||||
|
GRADLE_USER_HOME: '$CI_PROJECT_DIR/.gradle-home'
|
||||||
cache:
|
cache:
|
||||||
key: '$CI_PROJECT_ID-gradle'
|
key: '$CI_PROJECT_ID-gradle'
|
||||||
paths:
|
paths:
|
||||||
- .gradle/
|
- .gradle/
|
||||||
- ~/.gradle/caches/
|
- .gradle-home/
|
||||||
|
|
||||||
.on-commit: &on-commit
|
.on-commit: &on-commit
|
||||||
rules:
|
rules:
|
||||||
@@ -22,7 +24,7 @@ stages:
|
|||||||
.on-mr: &on-mr
|
.on-mr: &on-mr
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
allow_failure: true # deactivate after all issues (shown by checkstyle) have been resolved
|
allow_failure: false
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
checkstyle:
|
checkstyle:
|
||||||
@@ -30,7 +32,7 @@ checkstyle:
|
|||||||
stage: lint
|
stage: lint
|
||||||
image: gradle:9.3.1-jdk25
|
image: gradle:9.3.1-jdk25
|
||||||
script:
|
script:
|
||||||
- gradle checkstyleMain checkstyleTest
|
- gradle checkstyleMain checkstyleTest --configuration-cache --configuration-cache-problems=warn
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
artifacts:
|
artifacts:
|
||||||
when: always
|
when: always
|
||||||
@@ -47,8 +49,8 @@ checkstyle-mr:
|
|||||||
stage: lint
|
stage: lint
|
||||||
image: gradle:9.3.1-jdk25
|
image: gradle:9.3.1-jdk25
|
||||||
script:
|
script:
|
||||||
- gradle checkstyleMain checkstyleTest
|
- gradle checkstyleMain checkstyleTest --configuration-cache --configuration-cache-problems=warn
|
||||||
allow_failure: true
|
allow_failure: false
|
||||||
artifacts:
|
artifacts:
|
||||||
when: always
|
when: always
|
||||||
paths:
|
paths:
|
||||||
@@ -66,6 +68,7 @@ checkstyle-report:
|
|||||||
- job: checkstyle-mr
|
- job: checkstyle-mr
|
||||||
artifacts: true
|
artifacts: true
|
||||||
optional: true
|
optional: true
|
||||||
|
when: always
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
script:
|
script:
|
||||||
@@ -81,7 +84,7 @@ compile-check:
|
|||||||
stage: build
|
stage: build
|
||||||
image: gradle:9.3.1-jdk25
|
image: gradle:9.3.1-jdk25
|
||||||
script:
|
script:
|
||||||
- gradle compileTestJava
|
- gradle compileTestJava --configuration-cache --configuration-cache-problems=warn
|
||||||
needs: []
|
needs: []
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
@@ -89,12 +92,20 @@ compile-check:
|
|||||||
- if: '$CI_COMMIT_BRANCH'
|
- if: '$CI_COMMIT_BRANCH'
|
||||||
allow_failure: false
|
allow_failure: false
|
||||||
|
|
||||||
|
javadoc-check:
|
||||||
|
<<: [*gradle-cache, *on-mr]
|
||||||
|
stage: build
|
||||||
|
image: gradle:9.3.1-jdk25
|
||||||
|
script:
|
||||||
|
- gradle javaDoc --configuration-cache --configuration-cache-problems=warn
|
||||||
|
needs: []
|
||||||
|
|
||||||
test:
|
test:
|
||||||
<<: *gradle-cache
|
<<: *gradle-cache
|
||||||
stage: test
|
stage: test
|
||||||
image: gradle:9.3.1-jdk25
|
image: gradle:9.3.1-jdk25
|
||||||
script:
|
script:
|
||||||
- gradle test
|
- gradle test --configuration-cache --configuration-cache-problems=warn
|
||||||
artifacts:
|
artifacts:
|
||||||
when: always
|
when: always
|
||||||
reports:
|
reports:
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
### Checklist
|
### Checklist
|
||||||
- [ ] I reproduced the problem using the steps above
|
- [ ] I reproduced the problem using the steps above
|
||||||
- [ ] I searched documentation docs for relevant information
|
- [ ] I searched documentation for relevant information
|
||||||
- [ ] I added relevant labels
|
- [ ] I added relevant labels
|
||||||
|
|
||||||
/label ~bug
|
/label ~bug
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
<!-- Detailed description of the desired behavior -->
|
<!-- Detailed description of the desired behavior -->
|
||||||
|
|
||||||
### Checklist
|
### Checklist
|
||||||
- [ ] I reproduced the problem using the steps above
|
- [ ] I have described the function in detail
|
||||||
- [ ] I searched documentation docs for relevant information
|
- [ ] I searched docs for alternative implementations matching my needs
|
||||||
- [ ] I added relevant labels
|
- [ ] I added relevant labels
|
||||||
|
|
||||||
/label ~enhancement
|
/label ~enhancement
|
||||||
|
|||||||
@@ -108,3 +108,17 @@ tasks.register('fatJar', Jar) {
|
|||||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register('javadocJar', Jar) {
|
||||||
|
group = 'build'
|
||||||
|
description = 'Assembles a Javadoc JAR.'
|
||||||
|
dependsOn tasks.named('javadoc')
|
||||||
|
archiveClassifier = 'javadoc'
|
||||||
|
from(tasks.javadoc.destinationDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('build-cs108') {
|
||||||
|
group = 'build'
|
||||||
|
description = 'Produces executable JAR and Javadoc JAR for CS108.'
|
||||||
|
dependsOn tasks.named('fatJar'), tasks.named('javadocJar')
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import org.apache.logging.log4j.Logger;
|
|||||||
/**
|
/**
|
||||||
* Entry point for the Casono client application. Handles client startup and connection parameters.
|
* Entry point for the Casono client application. Handles client startup and connection parameters.
|
||||||
*
|
*
|
||||||
* <p>Standardkonstruktor für die Anwendung.
|
* <p>Default constructor for the application.
|
||||||
*/
|
*/
|
||||||
public class ClientApp {
|
public class ClientApp {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(ClientApp.class);
|
private static final Logger LOGGER = LogManager.getLogger(ClientApp.class);
|
||||||
|
|
||||||
/** Standardkonstruktor. */
|
/** Default constructor. */
|
||||||
public ClientApp() {
|
public ClientApp() {
|
||||||
// Standardkonstruktor
|
// Default constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import javafx.application.Application;
|
|||||||
/**
|
/**
|
||||||
* Launcher for the Casono main UI.
|
* Launcher for the Casono main UI.
|
||||||
*
|
*
|
||||||
* <p>Standardkonstruktor für die Anwendung.
|
* <p>Default constructor for the application.
|
||||||
*/
|
*/
|
||||||
public class Launcher {
|
public class Launcher {
|
||||||
|
|
||||||
/** Standardkonstruktor. */
|
/** Default constructor. */
|
||||||
public Launcher() {
|
public Launcher() {
|
||||||
// Standardkonstruktor
|
// Default constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ public class ChatController {
|
|||||||
chatScrollPane.vvalueProperty().bind(chatVBox.heightProperty());
|
chatScrollPane.vvalueProperty().bind(chatVBox.heightProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Standardkonstruktor. Wird von FXML verwendet. */
|
||||||
|
public ChatController() {
|
||||||
|
// default constructor for FXML
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Diese Methode wird vom Senden-Button oder Enter ausgelöst. Sie gibt die eigene Nachricht an
|
* Diese Methode wird vom Senden-Button oder Enter ausgelöst. Sie gibt die eigene Nachricht an
|
||||||
* das Netzwerkprotokoll weiter.
|
* das Netzwerkprotokoll weiter.
|
||||||
|
|||||||
@@ -15,11 +15,22 @@ import javafx.scene.layout.VBox;
|
|||||||
*/
|
*/
|
||||||
public class CasinoGameController {
|
public class CasinoGameController {
|
||||||
|
|
||||||
|
/** Standardkonstruktor. Wird von FXML verwendet. */
|
||||||
|
public CasinoGameController() {
|
||||||
|
// default constructor for FXML
|
||||||
|
}
|
||||||
|
|
||||||
@FXML private Label welcomeText;
|
@FXML private Label welcomeText;
|
||||||
@FXML private VBox casinoTable;
|
@FXML private VBox casinoTable;
|
||||||
|
|
||||||
// TODO: Test-Logik: wird durch echte Spielinteraktionen ersetzt,
|
// TODO: Test-Logik: wird durch echte Spielinteraktionen ersetzt,
|
||||||
// sobald die GameEngine fertig ist
|
// sobald die GameEngine fertig ist
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporäre Test-Methode, die bei Klick auf den Tisch eine Platzhalteraktion ausführt.
|
||||||
|
*
|
||||||
|
* <p>Wird in der finalen Implementierung durch die Spiel-Logik ersetzt.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public void onTableClick() {
|
public void onTableClick() {
|
||||||
welcomeText.setText("Einsatz akzeptiert!");
|
welcomeText.setText("Einsatz akzeptiert!");
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ import javafx.stage.Stage;
|
|||||||
*/
|
*/
|
||||||
public class CasinoGameUI extends Application {
|
public class CasinoGameUI extends Application {
|
||||||
|
|
||||||
|
/** Standardkonstruktor. */
|
||||||
|
public CasinoGameUI() {
|
||||||
|
// default no-arg constructor
|
||||||
|
}
|
||||||
|
|
||||||
private static final int DEFAULT_WIDTH = 1200;
|
private static final int DEFAULT_WIDTH = 1200;
|
||||||
private static final int DEFAULT_HEIGHT = 800;
|
private static final int DEFAULT_HEIGHT = 800;
|
||||||
|
|
||||||
@@ -29,7 +34,7 @@ public class CasinoGameUI extends Application {
|
|||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
FXMLLoader fxmlLoader =
|
FXMLLoader fxmlLoader =
|
||||||
new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/casinogameui.fxml"));
|
new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/Casinogameui.fxml"));
|
||||||
Scene scene = new Scene(fxmlLoader.load(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
Scene scene = new Scene(fxmlLoader.load(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||||
stage.setTitle("Casono (GAME)");
|
stage.setTitle("Casono (GAME)");
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import javafx.scene.shape.Rectangle;
|
|||||||
import javafx.scene.web.WebEngine;
|
import javafx.scene.web.WebEngine;
|
||||||
import javafx.scene.web.WebView;
|
import javafx.scene.web.WebView;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Experimenteller integrierter Browser für Casono.
|
* Experimenteller integrierter Browser für Casono.
|
||||||
@@ -50,13 +52,17 @@ import javafx.stage.Stage;
|
|||||||
*/
|
*/
|
||||||
public class CasinoBrowserController {
|
public class CasinoBrowserController {
|
||||||
|
|
||||||
|
/** Standardkonstruktor. Initialisiert den CasinoBrowserController. */
|
||||||
|
public CasinoBrowserController() {
|
||||||
|
// Intentionally left blank; controller initialization is FXML-driven.
|
||||||
|
}
|
||||||
|
|
||||||
private static final Set<String> TRUSTED_DOMAINS = new HashSet<>();
|
private static final Set<String> TRUSTED_DOMAINS = new HashSet<>();
|
||||||
|
|
||||||
private static final CookieManager COOKIE_MANAGER =
|
private static final CookieManager COOKIE_MANAGER =
|
||||||
new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
||||||
|
|
||||||
private static final org.apache.logging.log4j.Logger LOGGER =
|
private static final Logger LOGGER = LogManager.getLogger(CasinoBrowserController.class);
|
||||||
org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class);
|
|
||||||
|
|
||||||
private static final int LOGO_HEIGHT = 40;
|
private static final int LOGO_HEIGHT = 40;
|
||||||
private static final int CORNER_RADIUS = 40;
|
private static final int CORNER_RADIUS = 40;
|
||||||
@@ -354,8 +360,9 @@ public class CasinoBrowserController {
|
|||||||
fwdBtn.getStyleClass().add("gray-button");
|
fwdBtn.getStyleClass().add("gray-button");
|
||||||
fwdBtn.setOnAction(
|
fwdBtn.setOnAction(
|
||||||
e -> {
|
e -> {
|
||||||
if (engine.getHistory().getCurrentIndex()
|
int currentIndex = engine.getHistory().getCurrentIndex();
|
||||||
< engine.getHistory().getEntries().size() - 1) {
|
int lastIndex = engine.getHistory().getEntries().size() - 1;
|
||||||
|
if (currentIndex < lastIndex) {
|
||||||
engine.getHistory().go(1);
|
engine.getHistory().go(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -423,7 +430,7 @@ public class CasinoBrowserController {
|
|||||||
|
|
||||||
Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
|
Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
|
||||||
var css = CasinoBrowserController.class.getResource("/ui-structure/casinogameui.css");
|
var css = CasinoBrowserController.class.getResource("/ui-structure/Casinogameui.css");
|
||||||
|
|
||||||
if (css != null) {
|
if (css != null) {
|
||||||
scene.getStylesheets().add(css.toExternalForm());
|
scene.getStylesheets().add(css.toExternalForm());
|
||||||
@@ -512,10 +519,11 @@ public class CasinoBrowserController {
|
|||||||
|
|
||||||
alert.setTitle("Unbekannte Website");
|
alert.setTitle("Unbekannte Website");
|
||||||
alert.setHeaderText("Diese Website ist nicht bekannt");
|
alert.setHeaderText("Diese Website ist nicht bekannt");
|
||||||
alert.setContentText(
|
String content =
|
||||||
host
|
host
|
||||||
+ "\n\nDiese Seite ist nicht vom "
|
+ "\n\nDiese Seite ist nicht vom Casono Browser verifiziert.\n"
|
||||||
+ "Casono Browser verifiziert.\nMöchten Sie sie trotzdem öffnen?");
|
+ "Möchten Sie sie trotzdem öffnen?";
|
||||||
|
alert.setContentText(content);
|
||||||
|
|
||||||
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
||||||
Image logo = new Image(stream);
|
Image logo = new Image(stream);
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller für die interaktive Taskleiste innerhalb der Poker-UI.
|
* Controller für die interaktive Taskleiste innerhalb der Poker-UI.
|
||||||
@@ -16,8 +18,12 @@ import javafx.scene.layout.HBox;
|
|||||||
*/
|
*/
|
||||||
public class TaskbarController {
|
public class TaskbarController {
|
||||||
|
|
||||||
private static final org.apache.logging.log4j.Logger LOGGER =
|
/** Standardkonstruktor. Wird von FXML verwendet. */
|
||||||
org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class);
|
public TaskbarController() {
|
||||||
|
// default constructor for FXML
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(CasinoBrowserController.class);
|
||||||
|
|
||||||
@FXML private HBox taskbar;
|
@FXML private HBox taskbar;
|
||||||
@FXML private TextField taskbarInput;
|
@FXML private TextField taskbarInput;
|
||||||
@@ -92,15 +98,21 @@ public class TaskbarController {
|
|||||||
processBet();
|
processBet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wird aufgerufen, wenn der Exit-Button in der Taskleiste gedrückt wird.
|
|
||||||
*
|
|
||||||
* <p>TODO: Logik implementieren, um zur Lobby zurückzukehren, ohne die gesamte Anwendung zu
|
|
||||||
* schließen (kein System.exit/Platform.exit).
|
|
||||||
*/
|
|
||||||
@FXML
|
@FXML
|
||||||
private void onExitButtonClick() {
|
private void onExitButtonClick() {
|
||||||
Platform.exit();
|
javafx.application.Platform.runLater(
|
||||||
|
() -> {
|
||||||
|
// Close game stage
|
||||||
|
javafx.stage.Stage currentStage =
|
||||||
|
(javafx.stage.Stage) taskbar.getScene().getWindow();
|
||||||
|
currentStage.close();
|
||||||
|
// Lobby-UI starten
|
||||||
|
try {
|
||||||
|
new Casinomainui().start(new javafx.stage.Stage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Fehler beim Starten der Lobby-UI: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import javafx.stage.Stage;
|
|||||||
/**
|
/**
|
||||||
* JavaFX Application class for the Casono main UI.
|
* JavaFX Application class for the Casono main UI.
|
||||||
*
|
*
|
||||||
* <p>Standardkonstruktor für die Anwendung.
|
* <p>Default constructor for the application.
|
||||||
*/
|
*/
|
||||||
public class Casinomainui extends Application {
|
public class Casinomainui extends Application {
|
||||||
|
|
||||||
/** Standardkonstruktor. */
|
/** Default constructor. */
|
||||||
public Casinomainui() {
|
public Casinomainui() {
|
||||||
// Standardkonstruktor
|
// Default constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int SCENE_WIDTH = 1200;
|
private static final int SCENE_WIDTH = 1200;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public class CasinomainuiController {
|
|||||||
private LobbyButtonGridManager gridManager;
|
private LobbyButtonGridManager gridManager;
|
||||||
private int nextButtonId = 1;
|
private int nextButtonId = 1;
|
||||||
|
|
||||||
|
/** Default constructor for dependency injection by FXMLLoader. */
|
||||||
public CasinomainuiController() {
|
public CasinomainuiController() {
|
||||||
// Default constructor
|
// Default constructor
|
||||||
}
|
}
|
||||||
@@ -39,7 +40,7 @@ public class CasinomainuiController {
|
|||||||
subtitleLabel.setText("Texas Hold'em Poker");
|
subtitleLabel.setText("Texas Hold'em Poker");
|
||||||
logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm()));
|
logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm()));
|
||||||
|
|
||||||
translationManager = new LobbyButtonTranslationManager();
|
translationManager = LobbyButtonTranslationManager.getInstance();
|
||||||
gridManager =
|
gridManager =
|
||||||
new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager);
|
new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager);
|
||||||
casinoTable.getChildren().clear();
|
casinoTable.getChildren().clear();
|
||||||
@@ -57,7 +58,7 @@ public class CasinomainuiController {
|
|||||||
@FXML
|
@FXML
|
||||||
public void handleCreateLobbyButton() {
|
public void handleCreateLobbyButton() {
|
||||||
if (translationManager.isFull()) {
|
if (translationManager.isFull()) {
|
||||||
LOGGER.warn("Grid voll! Keine weiteren Lobbys moeglich.");
|
LOGGER.warn("Grid is full! No more lobbies available.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int buttonId = nextButtonId++;
|
int buttonId = nextButtonId++;
|
||||||
@@ -67,7 +68,7 @@ public class CasinomainuiController {
|
|||||||
LOGGER.info("ButtonID: {}, LobbyID: {}", buttonId, lobbyId);
|
LOGGER.info("ButtonID: {}, LobbyID: {}", buttonId, lobbyId);
|
||||||
gridManager.renderLobbyButtons();
|
gridManager.renderLobbyButtons();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Fehler beim Hinzufügen: {}", e.getMessage());
|
LOGGER.error("Error while adding lobby button: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import org.apache.logging.log4j.Logger;
|
|||||||
* ButtonID to LobbyID.
|
* ButtonID to LobbyID.
|
||||||
*/
|
*/
|
||||||
public class LobbyButtonGridManager {
|
public class LobbyButtonGridManager {
|
||||||
|
private static final double BUTTON_WIDTH_MARGIN = 20.0;
|
||||||
|
private static final double BUTTON_MIN_SIZE = 10.0;
|
||||||
private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
|
private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
|
||||||
|
|
||||||
/** GridPane for the button grid. */
|
/** GridPane for the button grid. */
|
||||||
@@ -46,7 +48,8 @@ public class LobbyButtonGridManager {
|
|||||||
public LobbyButtonGridManager(
|
public LobbyButtonGridManager(
|
||||||
GridPane gridPane, LobbyButtonTranslationManager translationManager) {
|
GridPane gridPane, LobbyButtonTranslationManager translationManager) {
|
||||||
this.gridPane = gridPane;
|
this.gridPane = gridPane;
|
||||||
this.translationManager = translationManager;
|
// Singleton immer verwenden
|
||||||
|
this.translationManager = LobbyButtonTranslationManager.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,8 +68,21 @@ public class LobbyButtonGridManager {
|
|||||||
int buttonId = entry.getKey();
|
int buttonId = entry.getKey();
|
||||||
Button btn = new Button();
|
Button btn = new Button();
|
||||||
btn.setId("lobbyBtn-" + buttonId);
|
btn.setId("lobbyBtn-" + buttonId);
|
||||||
btn.setGraphic(
|
ImageView imageView =
|
||||||
new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH))));
|
new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH)));
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
// Dynamische Breite: Bindung an die Zellengröße
|
||||||
|
imageView
|
||||||
|
.fitWidthProperty()
|
||||||
|
.bind(gridPane.widthProperty().divide(COLS).subtract(BUTTON_WIDTH_MARGIN));
|
||||||
|
imageView.setSmooth(true);
|
||||||
|
btn.setGraphic(imageView);
|
||||||
|
btn.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
btn.setMaxHeight(Double.MAX_VALUE);
|
||||||
|
btn.setMinWidth(BUTTON_MIN_SIZE);
|
||||||
|
btn.setMinHeight(BUTTON_MIN_SIZE);
|
||||||
|
GridPane.setHgrow(btn, javafx.scene.layout.Priority.ALWAYS);
|
||||||
|
GridPane.setVgrow(btn, javafx.scene.layout.Priority.ALWAYS);
|
||||||
btn.setOnAction(
|
btn.setOnAction(
|
||||||
e -> {
|
e -> {
|
||||||
Integer lobbyId = translationManager.getLobbyIdForButton(buttonId);
|
Integer lobbyId = translationManager.getLobbyIdForButton(buttonId);
|
||||||
@@ -99,8 +115,22 @@ public class LobbyButtonGridManager {
|
|||||||
* @param lobbyId The lobbyId to join
|
* @param lobbyId The lobbyId to join
|
||||||
*/
|
*/
|
||||||
public void joinLobby(int lobbyId) {
|
public void joinLobby(int lobbyId) {
|
||||||
// TODO: Replace with actual join logic
|
// Game-UI starten und Lobby-UI schließen
|
||||||
LOGGER.info("Joining lobby: {}", lobbyId);
|
LOGGER.info("Joining lobby: {}", lobbyId);
|
||||||
|
javafx.application.Platform.runLater(
|
||||||
|
() -> {
|
||||||
|
// Lobby-Stage schließen
|
||||||
|
javafx.stage.Stage currentStage =
|
||||||
|
(javafx.stage.Stage) gridPane.getScene().getWindow();
|
||||||
|
currentStage.close();
|
||||||
|
// Game-UI starten
|
||||||
|
try {
|
||||||
|
new ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI()
|
||||||
|
.start(new javafx.stage.Stage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Fehler beim Starten der Game-UI: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,36 +4,53 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verwaltet das Mapping zwischen Button-IDs und Lobby-IDs rein im Speicher. Keine Dateioperationen,
|
* Manages the mapping between Button IDs and Lobby IDs in memory only. No file operations,
|
||||||
* nur Laufzeitdatenstruktur.
|
* runtime-only data structure.
|
||||||
*/
|
*/
|
||||||
public class LobbyButtonTranslationManager {
|
public class LobbyButtonTranslationManager {
|
||||||
/** Maximale Anzahl an Buttons/Lobbys */
|
|
||||||
|
// Singleton instance
|
||||||
|
private static LobbyButtonTranslationManager instance;
|
||||||
|
|
||||||
|
// Singleton access
|
||||||
|
/**
|
||||||
|
* Returns the singleton instance of the manager.
|
||||||
|
*
|
||||||
|
* @return the single instance of {@code LobbyButtonTranslationManager}
|
||||||
|
*/
|
||||||
|
public static LobbyButtonTranslationManager getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new LobbyButtonTranslationManager();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Maximum number of buttons/lobbies */
|
||||||
private static final int MAX_BUTTONS = 8;
|
private static final int MAX_BUTTONS = 8;
|
||||||
|
|
||||||
/** Zuordnung ButtonID → LobbyID */
|
/** Mapping ButtonID → LobbyID */
|
||||||
private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>();
|
private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>();
|
||||||
|
|
||||||
/** Konstruktor: initialisiert die Zuordnung leer. */
|
/** Private constructor for the singleton pattern */
|
||||||
public LobbyButtonTranslationManager() {
|
private LobbyButtonTranslationManager() {
|
||||||
// Zuordnung bleibt leer beim Start
|
// Mapping is empty at startup
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft, ob das Grid voll ist (MAX_BUTTONS erreicht).
|
* Checks whether the grid is full (MAX_BUTTONS reached).
|
||||||
*
|
*
|
||||||
* @return true, wenn Grid voll; sonst false
|
* @return true if the grid is full; otherwise false
|
||||||
*/
|
*/
|
||||||
public boolean isFull() {
|
public boolean isFull() {
|
||||||
return buttonIdToLobbyId.size() >= MAX_BUTTONS;
|
return buttonIdToLobbyId.size() >= MAX_BUTTONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fügt eine Zuordnung ButtonID → LobbyID hinzu.
|
* Adds a mapping ButtonID → LobbyID.
|
||||||
*
|
*
|
||||||
* @param buttonId Die ID des Buttons
|
* @param buttonId the ID of the button
|
||||||
* @param lobbyId Die ID der Lobby
|
* @param lobbyId the ID of the lobby
|
||||||
* @throws Exception wenn das Grid voll ist
|
* @throws Exception when the grid is full
|
||||||
*/
|
*/
|
||||||
public void addLobbyButton(int buttonId, int lobbyId) throws Exception {
|
public void addLobbyButton(int buttonId, int lobbyId) throws Exception {
|
||||||
if (isFull()) {
|
if (isFull()) {
|
||||||
@@ -43,28 +60,28 @@ public class LobbyButtonTranslationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entfernt eine Zuordnung für die gegebene ButtonID.
|
* Removes the mapping for the given ButtonID.
|
||||||
*
|
*
|
||||||
* @param buttonId Die ID des zu entfernenden Buttons
|
* @param buttonId the ID of the button to remove
|
||||||
*/
|
*/
|
||||||
public void removeLobbyButton(int buttonId) {
|
public void removeLobbyButton(int buttonId) {
|
||||||
buttonIdToLobbyId.remove(buttonId);
|
buttonIdToLobbyId.remove(buttonId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die LobbyID für eine gegebene ButtonID zurück.
|
* Returns the LobbyID for a given ButtonID.
|
||||||
*
|
*
|
||||||
* @param buttonId Die ButtonID
|
* @param buttonId the ButtonID
|
||||||
* @return Die zugehoerige LobbyID oder null, falls nicht vorhanden
|
* @return the associated LobbyID or null if not present
|
||||||
*/
|
*/
|
||||||
public Integer getLobbyIdForButton(int buttonId) {
|
public Integer getLobbyIdForButton(int buttonId) {
|
||||||
return buttonIdToLobbyId.get(buttonId);
|
return buttonIdToLobbyId.get(buttonId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die gesamte Zuordnung ButtonID → LobbyID zurück.
|
* Returns the full mapping ButtonID → LobbyID.
|
||||||
*
|
*
|
||||||
* @return Map aller Zuordnungen
|
* @return Map of all mappings
|
||||||
*/
|
*/
|
||||||
public Map<Integer, Integer> getButtonIdToLobbyId() {
|
public Map<Integer, Integer> getButtonIdToLobbyId() {
|
||||||
return buttonIdToLobbyId;
|
return buttonIdToLobbyId;
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package ch.unibas.dmi.dbis.cs108.casono.server;
|
|||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionDisconnectJob;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -17,9 +18,12 @@ import org.apache.logging.log4j.Logger;
|
|||||||
|
|
||||||
/** Application class for starting the server. */
|
/** Application class for starting the server. */
|
||||||
public class ServerApp {
|
public class ServerApp {
|
||||||
public static final int USER_CLEANUP_JOB_DELAY = 0;
|
private static final int USER_CLEANUP_JOB_DELAY = 0;
|
||||||
public static final int USER_CLEANUP_JOB_PERIOD = 10;
|
private static final int USER_CLEANUP_JOB_PERIOD = 10;
|
||||||
public static final int USER_CLEANUP_JOB_RECONNECT_THRESHOLD = 10;
|
private static final int USER_CLEANUP_JOB_RECONNECT_THRESHOLD = 10;
|
||||||
|
private static final int SESSION_DISCONNECT_JOB_DELAY = 0;
|
||||||
|
private static final int SESSION_DISCONNECT_JOB_PERIOD = 2;
|
||||||
|
private static final int SESSION_DISCONNECT_JOB_TIMEOUT = 5;
|
||||||
|
|
||||||
public static void start(String arg) {
|
public static void start(String arg) {
|
||||||
int port = Integer.parseInt(arg);
|
int port = Integer.parseInt(arg);
|
||||||
@@ -45,6 +49,14 @@ public class ServerApp {
|
|||||||
USER_CLEANUP_JOB_DELAY,
|
USER_CLEANUP_JOB_DELAY,
|
||||||
USER_CLEANUP_JOB_PERIOD,
|
USER_CLEANUP_JOB_PERIOD,
|
||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
|
scheduler.scheduleAtFixedRate(
|
||||||
|
new SessionDisconnectJob(
|
||||||
|
sessionManager,
|
||||||
|
eventBus,
|
||||||
|
Duration.ofSeconds(SESSION_DISCONNECT_JOB_TIMEOUT)),
|
||||||
|
SESSION_DISCONNECT_JOB_DELAY,
|
||||||
|
SESSION_DISCONNECT_JOB_PERIOD,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
|
|
||||||
networkManager.start();
|
networkManager.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ public class UserRegistry {
|
|||||||
*
|
*
|
||||||
* @param sessionId the session ID of the disconnected client
|
* @param sessionId the session ID of the disconnected client
|
||||||
*/
|
*/
|
||||||
// TODO: Add to EventRegistry with DisconnectEvent
|
|
||||||
public synchronized void onDisconnect(SessionId sessionId) {
|
public synchronized void onDisconnect(SessionId sessionId) {
|
||||||
User user = bySessionId.remove(sessionId);
|
User user = bySessionId.remove(sessionId);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.command;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.Request;
|
|
||||||
|
|
||||||
public interface CommandHandler<T extends Request> {
|
|
||||||
void execute(T request);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
|
|
||||||
|
public interface CommandHandler<T extends Request> {
|
||||||
|
void execute(T request);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.command;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.Request;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.command;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
|
||||||
|
|
||||||
public class UnknownRequestException extends RuntimeException {
|
public class UnknownRequestException extends RuntimeException {
|
||||||
private final String requestName;
|
private final String requestName;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser to convert the PrimitiveRequest to a Request and performing checks for required fields and
|
||||||
|
* data types
|
||||||
|
*/
|
||||||
|
public interface CommandParser<T extends Request> {
|
||||||
|
/**
|
||||||
|
* Parses the provided PrimitiveRequest into a command-specific request
|
||||||
|
*
|
||||||
|
* @param primitiveRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
T parse(PrimitiveRequest primitiveRequest);
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing;
|
||||||
|
|
||||||
|
/** Used in the PrimitiveRequest class to store the key of a parameter with its respective value */
|
||||||
|
public record RequestParameter(String key, String value) {}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when the CommandParserDispatcher has no registered handler for the provided
|
* Exception thrown when the CommandParserDispatcher has no registered handler for the provided
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parser to convert the PrimitiveRequest to a Request and performing checks for required fields and
|
|
||||||
* data types
|
|
||||||
*/
|
|
||||||
public interface CommandParser {
|
|
||||||
/**
|
|
||||||
* Parses the provided PrimitiveRequest into a command-specific request
|
|
||||||
*
|
|
||||||
* @param primitiveRequest
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Request parse(PrimitiveRequest primitiveRequest);
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
|
||||||
|
|
||||||
/** Used in the PrimitiveRequest class to store the key of a parameter with its respective value */
|
|
||||||
public record Parameter(String key, String value) {}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Created by the ProtocolParser to allow easy access to the request contents */
|
|
||||||
public record PrimitiveRequest(
|
|
||||||
RequestContext context, String command, List<Parameter> parameters) {}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public record RawRequest(String command, List<Parameter> parameters) {}
|
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.RawToken;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.Token;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer.RawToken;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.TokenClassifier;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer.Token;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.TokenType;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer.TokenClassifier;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.Tokenizer;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer.TokenType;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer.Tokenizer;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RawRequest;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -15,7 +17,7 @@ public class ProtocolParser {
|
|||||||
/**
|
/**
|
||||||
* Parses the payload of the provided RawPacket
|
* Parses the payload of the provided RawPacket
|
||||||
*
|
*
|
||||||
* @param packet the RawPacket containing the recieved data
|
* @param payload the payload to parse and check for syntax
|
||||||
* @return created PrimitiveRequest
|
* @return created PrimitiveRequest
|
||||||
*/
|
*/
|
||||||
public static RawRequest parse(String payload) {
|
public static RawRequest parse(String payload) {
|
||||||
@@ -24,7 +26,7 @@ public class ProtocolParser {
|
|||||||
|
|
||||||
Iterator<Token> iterator = tokens.iterator();
|
Iterator<Token> iterator = tokens.iterator();
|
||||||
String command = readCommand(iterator);
|
String command = readCommand(iterator);
|
||||||
List<Parameter> parameters = readParameters(iterator);
|
List<RequestParameter> parameters = readParameters(iterator);
|
||||||
|
|
||||||
return new RawRequest(command, parameters);
|
return new RawRequest(command, parameters);
|
||||||
}
|
}
|
||||||
@@ -50,8 +52,8 @@ public class ProtocolParser {
|
|||||||
* @param iterator
|
* @param iterator
|
||||||
* @return list containing all parsed parameters
|
* @return list containing all parsed parameters
|
||||||
*/
|
*/
|
||||||
private static List<Parameter> readParameters(Iterator<Token> iterator) {
|
private static List<RequestParameter> readParameters(Iterator<Token> iterator) {
|
||||||
List<Parameter> parameters = new ArrayList<>();
|
List<RequestParameter> parameters = new ArrayList<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
@@ -65,7 +67,7 @@ public class ProtocolParser {
|
|||||||
readSeperator(iterator.next());
|
readSeperator(iterator.next());
|
||||||
String value = readValue(iterator.next());
|
String value = readValue(iterator.next());
|
||||||
|
|
||||||
parameters.add(new Parameter(key, value));
|
parameters.add(new RequestParameter(key, value));
|
||||||
}
|
}
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
throw new ProtocolParserException("Ran out of tokens while reading parameter");
|
throw new ProtocolParserException("Ran out of tokens while reading parameter");
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser;
|
||||||
|
|
||||||
public class ProtocolParserException extends RuntimeException {
|
public class ProtocolParserException extends RuntimeException {
|
||||||
public ProtocolParserException(String message) {
|
public ProtocolParserException(String message) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
/** Represents a raw (unclassified) token in the tokenizer. */
|
/** Represents a raw (unclassified) token in the tokenizer. */
|
||||||
public record RawToken(RawTokenType type, String value, int line, int column) {}
|
public record RawToken(RawTokenType type, String value, int line, int column) {}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
public enum RawTokenType {
|
public enum RawTokenType {
|
||||||
WORD,
|
WORD,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
/** Represents a token in the tokenizer. */
|
/** Represents a token in the tokenizer. */
|
||||||
public record Token(TokenType type, String value, int line, int column) {}
|
public record Token(TokenType type, String value, int line, int column) {}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
/** Enumeration of token types used in the tokenizer. */
|
/** Enumeration of token types used in the tokenizer. */
|
||||||
public enum TokenType {
|
public enum TokenType {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||||
|
|
||||||
/** Exception thrown during tokenization. */
|
/** Exception thrown during tokenization. */
|
||||||
public class TokenizerException extends RuntimeException {
|
public class TokenizerException extends RuntimeException {
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Created by the ProtocolParser to allow easy access to the request contents */
|
||||||
|
public record PrimitiveRequest(
|
||||||
|
RequestContext context, String command, List<RequestParameter> parameters) {}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record RawRequest(String command, List<RequestParameter> parameters) {}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor;
|
||||||
|
|
||||||
|
/** Exception thrown when a required parameter key is not found. */
|
||||||
|
public class MissingParameterException extends RuntimeException {
|
||||||
|
private final String parameterKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception for a missing required parameter.
|
||||||
|
*
|
||||||
|
* @param message human-readable description of the missing parameter
|
||||||
|
* @param parameterKey key of the parameter that could not be found
|
||||||
|
*/
|
||||||
|
public MissingParameterException(String message, String parameterKey) {
|
||||||
|
super(message);
|
||||||
|
this.parameterKey = parameterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the missing parameter key.
|
||||||
|
*
|
||||||
|
* @return key of the parameter that could not be found
|
||||||
|
*/
|
||||||
|
public String getParameterKey() {
|
||||||
|
return parameterKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor;
|
||||||
|
|
||||||
|
/** Exception thrown when a parameter value cannot be converted to the requested type. */
|
||||||
|
public class ParameterParseException extends RuntimeException {
|
||||||
|
private final String parameterKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new parse exception with a root cause.
|
||||||
|
*
|
||||||
|
* @param message human-readable description of the parsing failure
|
||||||
|
* @param parameterKey key for whose value the error occured
|
||||||
|
* @param cause original exception thrown during parsing
|
||||||
|
*/
|
||||||
|
public ParameterParseException(String message, String parameterKey, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.parameterKey = parameterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the missing parameter key.
|
||||||
|
*
|
||||||
|
* @return key for whose value the error occured
|
||||||
|
*/
|
||||||
|
public String getParameterKey() {
|
||||||
|
return parameterKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides typed access to a request's parameters by indexing them by key.
|
||||||
|
*
|
||||||
|
* <p>Supports required and optional lookups, with optional conversion from {@link String} values to
|
||||||
|
* domain-specific types via parser functions.
|
||||||
|
*/
|
||||||
|
public class RequestParameterAccessor {
|
||||||
|
private final Map<String, String> index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an accessor
|
||||||
|
*
|
||||||
|
* @param parameters to use
|
||||||
|
*/
|
||||||
|
public RequestParameterAccessor(List<RequestParameter> parameters) {
|
||||||
|
this.index =
|
||||||
|
parameters.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toUnmodifiableMap(
|
||||||
|
RequestParameter::key, RequestParameter::value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw value for a required parameter key.
|
||||||
|
*
|
||||||
|
* @param key parameter key to look up
|
||||||
|
* @return raw parameter value
|
||||||
|
* @throws MissingParameterException if no parameter with the given key exists
|
||||||
|
*/
|
||||||
|
public String require(String key) throws MissingParameterException {
|
||||||
|
String value = index.get(key);
|
||||||
|
if (value == null) {
|
||||||
|
throw new MissingParameterException(
|
||||||
|
"Required parameter with key '" + key + "' is missing.", key);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a parsed value for a required parameter key.
|
||||||
|
*
|
||||||
|
* @param key parameter key to look up
|
||||||
|
* @param parser parser used to convert the raw value
|
||||||
|
* @param <T> target type returned by the parser
|
||||||
|
* @return parsed parameter value
|
||||||
|
* @throws MissingParameterException if no parameter with the given key exists
|
||||||
|
* @throws ParameterParseException if parsing the raw value fails
|
||||||
|
*/
|
||||||
|
public <T> T require(String key, ThrowingParser<T> parser)
|
||||||
|
throws MissingParameterException, ParameterParseException {
|
||||||
|
String value = require(key);
|
||||||
|
try {
|
||||||
|
return parser.parse(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ParameterParseException(
|
||||||
|
"Error while parsing '" + key + "' with specified parser", key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw value for a parameter key or the provided default value if missing.
|
||||||
|
*
|
||||||
|
* @param key parameter key to look up
|
||||||
|
* @param defaultValue value returned when the key does not exist
|
||||||
|
* @return found parameter value or {@code defaultValue} if absent
|
||||||
|
*/
|
||||||
|
public String optional(String key, String defaultValue) {
|
||||||
|
String value = index.get(key);
|
||||||
|
if (value == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a parsed value for a parameter key or the provided default value if missing.
|
||||||
|
*
|
||||||
|
* @param key parameter key to look up
|
||||||
|
* @param defaultValue value returned when the key does not exist
|
||||||
|
* @param parser parser used to convert the raw value
|
||||||
|
* @param <T> target type returned by the parser
|
||||||
|
* @return parsed parameter value or {@code defaultValue} if absent
|
||||||
|
* @throws ParameterParseException if parsing the raw value fails
|
||||||
|
*/
|
||||||
|
public <T> T optional(String key, T defaultValue, ThrowingParser<T> parser)
|
||||||
|
throws ParameterParseException {
|
||||||
|
String value = index.get(key);
|
||||||
|
if (value == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return parser.parse(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ParameterParseException(
|
||||||
|
"Error while parsing '" + key + "' with specified parser", key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a parameter with the given key exists.
|
||||||
|
*
|
||||||
|
* @param key parameter key to check
|
||||||
|
* @return {@code true} if the key exists, otherwise {@code false}
|
||||||
|
*/
|
||||||
|
public boolean containsKey(String key) {
|
||||||
|
return index.containsKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional parser interface used to convert a raw string parameter into a target type.
|
||||||
|
*
|
||||||
|
* @param <T> target type produced by the parser
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
interface ThrowingParser<T> {
|
||||||
|
/**
|
||||||
|
* Parses the provided raw parameter value.
|
||||||
|
*
|
||||||
|
* @param value raw parameter value
|
||||||
|
* @return parsed value
|
||||||
|
* @throws Exception if the value cannot be parsed
|
||||||
|
*/
|
||||||
|
T parse(String value) throws Exception;
|
||||||
|
}
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||||
|
|
||||||
/** Response representing an error outcome for a client's request. */
|
/** Response representing an error outcome for a client's request. */
|
||||||
public class ErrorResponse extends Response {
|
public class ErrorResponse extends Response {
|
||||||
/**
|
/**
|
||||||
* Construct an error response with a code and message.
|
* Construct an error response with a code and message.
|
||||||
*
|
*
|
||||||
* @param sessionId the target session id
|
* @param context the RequestContext of the request
|
||||||
* @param requestId the originating request id
|
|
||||||
* @param errorCode a short error code identifying the failure
|
* @param errorCode a short error code identifying the failure
|
||||||
* @param errorMessage a human readable error message
|
* @param errorMessage a human readable error message
|
||||||
*/
|
*/
|
||||||
public ErrorResponse(
|
public ErrorResponse(RequestContext context, String errorCode, String errorMessage) {
|
||||||
SessionId sessionId, int requestId, String errorCode, String errorMessage) {
|
|
||||||
super(
|
super(
|
||||||
sessionId,
|
context,
|
||||||
requestId,
|
|
||||||
ResponseBody.builder().param("CODE", errorCode).param("MSG", errorMessage).build());
|
ResponseBody.builder().param("CODE", errorCode).param("MSG", errorMessage).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple success response with an empty body.
|
||||||
|
*
|
||||||
|
* <p>Use this to acknowledge successful requests that do not carry additional payload data.
|
||||||
|
*/
|
||||||
|
public class OkResponse extends SuccessResponse {
|
||||||
|
/**
|
||||||
|
* Create a minimal successful response (no body content).
|
||||||
|
*
|
||||||
|
* @param context the RequestContext of the request
|
||||||
|
*/
|
||||||
|
public OkResponse(RequestContext context) {
|
||||||
|
super(context, ResponseBody.builder().build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
||||||
|
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
||||||
|
|
||||||
/** Abstract base class for all server responses sent to clients. */
|
/** Abstract base class for all server responses sent to clients. */
|
||||||
public abstract class Response {
|
public abstract class Response {
|
||||||
private final SessionId sessionId;
|
private final RequestContext context;
|
||||||
private final int requestId;
|
|
||||||
private final ResponseBody body;
|
private final ResponseBody body;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code Response}.
|
* Create a new {@code Response}.
|
||||||
*
|
*
|
||||||
* @param sessionId the id of the session this response targets
|
* @param context the RequestContext of the request
|
||||||
* @param requestId the request identifier this response corresponds to
|
|
||||||
* @param body the structured response body
|
* @param body the structured response body
|
||||||
*/
|
*/
|
||||||
protected Response(SessionId sessionId, int requestId, ResponseBody body) {
|
protected Response(RequestContext context, ResponseBody body) {
|
||||||
this.sessionId = sessionId;
|
this.context = context;
|
||||||
this.requestId = requestId;
|
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,12 +28,12 @@ public abstract class Response {
|
|||||||
public abstract String prefix();
|
public abstract String prefix();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the session id that should receive this response.
|
* Returns the session id of the session that should receive this response.
|
||||||
*
|
*
|
||||||
* @return the target {@link SessionId}
|
* @return the target {@link SessionId}
|
||||||
*/
|
*/
|
||||||
public SessionId getSessionId() {
|
public SessionId getSessionId() {
|
||||||
return sessionId;
|
return context.sessionId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,7 +42,7 @@ public abstract class Response {
|
|||||||
* @return the numeric request id
|
* @return the numeric request id
|
||||||
*/
|
*/
|
||||||
public int getRequestId() {
|
public int getRequestId() {
|
||||||
return requestId;
|
return context.requestId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract {@link Response} specialization indicating a successful outcome.
|
* Abstract {@link Response} specialization indicating a successful outcome.
|
||||||
@@ -12,12 +13,11 @@ public abstract class SuccessResponse extends Response {
|
|||||||
/**
|
/**
|
||||||
* Create a successful response with the provided body.
|
* Create a successful response with the provided body.
|
||||||
*
|
*
|
||||||
* @param sessionId the session id this response targets
|
* @param context the RequestContext of the request
|
||||||
* @param requestId the originating request id
|
|
||||||
* @param body the response body
|
* @param body the response body
|
||||||
*/
|
*/
|
||||||
protected SuccessResponse(SessionId sessionId, int requestId, ResponseBody body) {
|
protected SuccessResponse(RequestContext context, ResponseBody body) {
|
||||||
super(sessionId, requestId, body);
|
super(context, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marker interface for elements that may appear in a {@link ResponseBody}.
|
* Marker interface for elements that may appear in a {@link ResponseBody}.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A parameter node stored in a {@link ResponseBody}.
|
* A parameter node stored in a {@link ResponseBody}.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher;
|
||||||
|
|
||||||
|
public class ResponseDispatchException extends RuntimeException {
|
||||||
|
public ResponseDispatchException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||||
|
|
||||||
@@ -25,12 +27,17 @@ public class ResponseDispatcher {
|
|||||||
* the target session's response queue.
|
* the target session's response queue.
|
||||||
*
|
*
|
||||||
* @param response the response to dispatch
|
* @param response the response to dispatch
|
||||||
* @throws InterruptedException if the thread is interrupted while waiting to enqueue the
|
* @throws ResponseDispatchException wraps any exceptions that occur during dispatching, such as
|
||||||
* primitive response
|
* the {@link InterruptedException}
|
||||||
*/
|
*/
|
||||||
public void dispatch(Response response) throws InterruptedException {
|
public void dispatch(Response response) {
|
||||||
PrimitiveResponse primitiveResponse = ResponseEncoder.encode(response);
|
PrimitiveResponse primitiveResponse = ResponseEncoder.encode(response);
|
||||||
Session session = sessionManager.getSessionById(response.getSessionId());
|
Session session = sessionManager.getSessionById(response.getSessionId());
|
||||||
|
try {
|
||||||
session.getResponseQueue().put(primitiveResponse);
|
session.getResponseQueue().put(primitiveResponse);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new ResponseDispatchException("Interrupted while dispatching response", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBlock;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseNode;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseParameter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility responsible for encoding a {@link Response} into a protocol payload string and wrapping
|
* Utility responsible for encoding a {@link Response} into a protocol payload string and wrapping
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.response;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple success response with an empty body.
|
|
||||||
*
|
|
||||||
* <p>Use this to acknowledge successful requests that do not carry additional payload data.
|
|
||||||
*/
|
|
||||||
public class OkResponse extends SuccessResponse {
|
|
||||||
/**
|
|
||||||
* Create a minimal successful response (no body content).
|
|
||||||
*
|
|
||||||
* @param sessionId the target session id
|
|
||||||
* @param requestId the originating request id
|
|
||||||
*/
|
|
||||||
public OkResponse(SessionId sessionId, int requestId) {
|
|
||||||
super(sessionId, requestId, ResponseBody.builder().build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.response.PrimitiveResponse;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
||||||
import java.io.IOException;
|
import java.time.Instant;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
/** Represents a client session in the network server. */
|
/** Represents a client session in the network server. */
|
||||||
public class Session {
|
public class Session {
|
||||||
private final SessionId id;
|
private final SessionId id;
|
||||||
|
private Instant lastActivity;
|
||||||
private final TransportLayer transport;
|
private final TransportLayer transport;
|
||||||
private final BlockingQueue<PrimitiveResponse> responseQueue;
|
private final BlockingQueue<PrimitiveResponse> responseQueue;
|
||||||
private final CommandParserDispatcher dispatcher;
|
private final CommandParserDispatcher dispatcher;
|
||||||
@@ -23,7 +24,6 @@ public class Session {
|
|||||||
*
|
*
|
||||||
* @param transport the transport layer for communication
|
* @param transport the transport layer for communication
|
||||||
* @param eventBus the event bus for publishing events
|
* @param eventBus the event bus for publishing events
|
||||||
* @throws IOException if an I/O error occurs during initialization
|
|
||||||
*/
|
*/
|
||||||
public Session(
|
public Session(
|
||||||
TransportLayer transport,
|
TransportLayer transport,
|
||||||
@@ -31,6 +31,7 @@ public class Session {
|
|||||||
CommandParserDispatcher dispatcher,
|
CommandParserDispatcher dispatcher,
|
||||||
CommandRouter router) {
|
CommandRouter router) {
|
||||||
this.id = new SessionId();
|
this.id = new SessionId();
|
||||||
|
this.lastActivity = Instant.now();
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this.router = router;
|
this.router = router;
|
||||||
@@ -46,6 +47,20 @@ public class Session {
|
|||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the timestamp of the last inbound activity for this session.
|
||||||
|
*
|
||||||
|
* @return an {@link Instant} representing the time of the last inbound activity
|
||||||
|
*/
|
||||||
|
public Instant getLastInboundActivity() {
|
||||||
|
return lastActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Updates the timestamp of the last inbound activity for this session. */
|
||||||
|
public void updateLastInboundActivity() {
|
||||||
|
this.lastActivity = Instant.now();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the TransportLayer of this session
|
* Returns the TransportLayer of this session
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
public class SessionDisconnectJob implements Runnable {
|
||||||
|
private final Logger logger;
|
||||||
|
private final SessionManager sessionManager;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final Duration timeoutThreshold;
|
||||||
|
|
||||||
|
public SessionDisconnectJob(
|
||||||
|
SessionManager sessionManager, EventBus eventBus, Duration timeoutThreshold) {
|
||||||
|
this.logger = LogManager.getLogger(SessionDisconnectJob.class);
|
||||||
|
this.sessionManager = sessionManager;
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.timeoutThreshold = timeoutThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logger.debug("Job started.");
|
||||||
|
Instant threshold = Instant.now().minus(timeoutThreshold);
|
||||||
|
|
||||||
|
for (Session session : sessionManager.getAllSessions()) {
|
||||||
|
if (session.getLastInboundActivity().isBefore(threshold)) {
|
||||||
|
eventBus.publish(new DisconnectEvent(session.getId()));
|
||||||
|
logger.info(
|
||||||
|
"Initiated disconnect of {}, as it hasn't been active since a while",
|
||||||
|
session.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Job finished.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ public class SessionId {
|
|||||||
/**
|
/**
|
||||||
* Creates a new SessionId with the specified UUID.
|
* Creates a new SessionId with the specified UUID.
|
||||||
*
|
*
|
||||||
* @param UUID to use for this SessionId
|
* @param value UUID to use for this SessionId
|
||||||
*/
|
*/
|
||||||
public SessionId(UUID value) {
|
public SessionId(UUID value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
@@ -86,7 +88,7 @@ public class SessionManager {
|
|||||||
/**
|
/**
|
||||||
* Handler for the DisconnectEvent
|
* Handler for the DisconnectEvent
|
||||||
*
|
*
|
||||||
* @param id of the session that disconnected
|
* @param event the DisconnectEvent to handle
|
||||||
*/
|
*/
|
||||||
public void onDisconnect(DisconnectEvent event) {
|
public void onDisconnect(DisconnectEvent event) {
|
||||||
logger.debug("Recieved DisconnectEvent event for session {}", event.sessionId().value());
|
logger.debug("Recieved DisconnectEvent event for session {}", event.sessionId().value());
|
||||||
@@ -109,4 +111,8 @@ public class SessionManager {
|
|||||||
|
|
||||||
return handle.session();
|
return handle.session();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<Session> getAllSessions() {
|
||||||
|
return sessions.values().stream().map(SessionHandle::session).collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.CommandRouter;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.UnknownCommandException;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.CommandParserDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.ProtocolParser;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.PrimitiveRequest;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.ProtocolParserException;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.ProtocolParser;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer.TokenizerException;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.ProtocolParserException;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.RawRequest;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RawRequest;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.Request;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.RequestContext;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatchException;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseEncoder;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.TokenizerException;
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@@ -41,34 +46,79 @@ public class SessionReader implements Runnable {
|
|||||||
public void run() {
|
public void run() {
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
RawPacket rawPacket = null;
|
RawPacket rawPacket = null;
|
||||||
|
RawRequest rawRequest = null;
|
||||||
|
RequestContext requestContext = null;
|
||||||
try {
|
try {
|
||||||
|
// Step 1: Read from transport
|
||||||
rawPacket = transport.read();
|
rawPacket = transport.read();
|
||||||
|
session.updateLastInboundActivity();
|
||||||
logger.debug("Recieved: {}", rawPacket);
|
logger.debug("Recieved: {}", rawPacket);
|
||||||
|
requestContext = new RequestContext(session.getId(), rawPacket.requestId());
|
||||||
|
|
||||||
RawRequest rawRequest = ProtocolParser.parse(rawPacket.payload());
|
// Step 2: Syntax validation and conversion into transport object
|
||||||
|
rawRequest = ProtocolParser.parse(rawPacket.payload());
|
||||||
logger.debug("Parsed request to {}", rawRequest);
|
logger.debug("Parsed request to {}", rawRequest);
|
||||||
|
|
||||||
RequestContext requestContext =
|
|
||||||
new RequestContext(session.getId(), rawPacket.requestId());
|
|
||||||
PrimitiveRequest primitiveRequest =
|
PrimitiveRequest primitiveRequest =
|
||||||
new PrimitiveRequest(
|
new PrimitiveRequest(
|
||||||
requestContext, rawRequest.command(), rawRequest.parameters());
|
requestContext, rawRequest.command(), rawRequest.parameters());
|
||||||
logger.debug("Converted to {}", primitiveRequest);
|
logger.debug("Converted to {}", primitiveRequest);
|
||||||
|
|
||||||
|
// Step 3: Parse into Request and execute Request
|
||||||
Request request = dispatcher.parse(primitiveRequest);
|
Request request = dispatcher.parse(primitiveRequest);
|
||||||
|
|
||||||
router.execute(request);
|
router.execute(request);
|
||||||
} catch (EOFException e) {
|
} catch (EOFException e) {
|
||||||
logger.info("Client disconnected");
|
logger.info("Client disconnected");
|
||||||
eventBus.publish(new DisconnectEvent(session.getId()));
|
eventBus.publish(new DisconnectEvent(session.getId()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} catch (TokenizerException | ProtocolParserException e) {
|
} catch (TokenizerException | ProtocolParserException e) {
|
||||||
logger.error("Error occured while parsing request. RawPacket: {}", rawPacket, e);
|
logger.error("Error occured while parsing request. RawPacket: {}", rawPacket, e);
|
||||||
|
|
||||||
// TODO: Send error response to client
|
sendErrorResponse(
|
||||||
|
new ErrorResponse(
|
||||||
|
requestContext,
|
||||||
|
"PARSING_ERROR",
|
||||||
|
"Error occured during parsing. Likely due to malformed payload."));
|
||||||
|
|
||||||
|
} catch (UnknownCommandException e) {
|
||||||
|
logger.error("Recieved unknown command '{}' from client", rawRequest.command(), e);
|
||||||
|
sendErrorResponse(
|
||||||
|
new ErrorResponse(
|
||||||
|
requestContext,
|
||||||
|
"UNKNOWN_COMMAND",
|
||||||
|
"This command is unknown to the server."));
|
||||||
|
|
||||||
|
} catch (ResponseDispatchException e) {
|
||||||
|
logger.error(
|
||||||
|
"Unexpected ResponseDispatchException exception while dispatching request",
|
||||||
|
e);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Unexpected exception while reading from transport", e);
|
logger.error("Unexpected IO exception while reading from transport", e);
|
||||||
|
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
logger.error("Unexpected RuntimeException occured", e);
|
||||||
|
sendErrorResponse(
|
||||||
|
new ErrorResponse(
|
||||||
|
requestContext,
|
||||||
|
"INTERNAL_ERROR",
|
||||||
|
"Unexpected internal server error occured."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helperfunction to send ErrorResponse to client
|
||||||
|
*
|
||||||
|
* @param response to send to the client
|
||||||
|
*/
|
||||||
|
private void sendErrorResponse(ErrorResponse response) {
|
||||||
|
PrimitiveResponse primitiveResponse = ResponseEncoder.encode(response);
|
||||||
|
try {
|
||||||
|
session.getResponseQueue().put(primitiveResponse);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.error("Got interrupted while sending ErrorResponse to client.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.response.PrimitiveResponse;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ public class TcpTransport implements TransportLayer {
|
|||||||
int requestId = data.requestId();
|
int requestId = data.requestId();
|
||||||
byte[] rawPayload = data.payload().getBytes(StandardCharsets.UTF_8);
|
byte[] rawPayload = data.payload().getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
out.writeInt(requestId);
|
|
||||||
out.writeInt(rawPayload.length);
|
out.writeInt(rawPayload.length);
|
||||||
|
out.writeInt(requestId);
|
||||||
out.write(rawPayload);
|
out.write(rawPayload);
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 6.3 MiB |
|
After Width: | Height: | Size: 6.2 MiB |
|
After Width: | Height: | Size: 902 KiB |
|
After Width: | Height: | Size: 701 KiB |
|
After Width: | Height: | Size: 651 KiB |
|
After Width: | Height: | Size: 665 KiB |
|
After Width: | Height: | Size: 659 KiB |
|
After Width: | Height: | Size: 660 KiB |
|
After Width: | Height: | Size: 657 KiB |
|
After Width: | Height: | Size: 659 KiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 658 KiB |
|
After Width: | Height: | Size: 663 KiB |
|
After Width: | Height: | Size: 662 KiB |
|
After Width: | Height: | Size: 663 KiB |
|
After Width: | Height: | Size: 657 KiB |
|
After Width: | Height: | Size: 664 KiB |
|
After Width: | Height: | Size: 665 KiB |
|
After Width: | Height: | Size: 648 KiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 656 KiB |
|
After Width: | Height: | Size: 656 KiB |
|
After Width: | Height: | Size: 653 KiB |
|
After Width: | Height: | Size: 655 KiB |
|
After Width: | Height: | Size: 658 KiB |
|
After Width: | Height: | Size: 654 KiB |
|
After Width: | Height: | Size: 660 KiB |
|
After Width: | Height: | Size: 658 KiB |
|
After Width: | Height: | Size: 659 KiB |
|
After Width: | Height: | Size: 653 KiB |
|
After Width: | Height: | Size: 660 KiB |
|
After Width: | Height: | Size: 660 KiB |