Initial commit

This commit is contained in:
ngb 2022-02-06 21:41:45 +01:00
commit 53beb78043
8 changed files with 1189 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# ---> Java
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
Dokumente*
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# Engine-Alpha files
engine-alpha.log

148
DatabaseConnector.java Normal file
View File

@ -0,0 +1,148 @@
import java.sql.*;
/**
* <p>
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
* </p>
* <p>
* Klasse DatabaseConnector
* </p>
* <p>
* Ein Objekt der Klasse DatabaseConnector ermoeglicht die Abfrage und Manipulation
* einer SQLite-Datenbank.
* Beim Erzeugen des Objekts wird eine Datenbankverbindung aufgebaut, so dass
* anschließend SQL-Anweisungen an diese Datenbank gerichtet werden koennen.
* </p>
*
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
* @version 2016-01-24
*/
public class DatabaseConnector{
private java.sql.Connection connection;
private QueryResult currentQueryResult = null;
private String message = null;
/**
* Ein Objekt vom Typ DatabaseConnector wird erstellt, und eine Verbindung zur Datenbank
* wird aufgebaut. Mit den Parametern pIP und pPort werden die IP-Adresse und die
* Port-Nummer uebergeben, unter denen die Datenbank mit Namen pDatabase zu erreichen ist.
* Mit den Parametern pUsername und pPassword werden Benutzername und Passwort fuer die
* Datenbank uebergeben.
*/
public DatabaseConnector(String pIP, int pPort, String pDatabase, String pUsername, String pPassword){
//Eine Impementierung dieser Schnittstelle fuer SQLite ignoriert pID und pPort, da die Datenbank immer lokal ist.
//Auch pUsername und pPassword werden nicht verwendet, da SQLite sie nicht unterstuetzt.
try {
//Laden der Treiberklasse
Class.forName("org.sqlite.JDBC");
//Verbindung herstellen
connection = DriverManager.getConnection("jdbc:sqlite:"+pDatabase);
} catch (Exception e) {
message = e.getMessage();
}
}
/**
* Der Auftrag schickt den im Parameter pSQLStatement enthaltenen SQL-Befehl an die
* Datenbank ab.
* Handelt es sich bei pSQLStatement um einen SQL-Befehl, der eine Ergebnismenge
* liefert, so kann dieses Ergebnis anschließend mit der Methode getCurrentQueryResult
* abgerufen werden.
*/
public void executeStatement(String pSQLStatement){
//Altes Ergebnis loeschen
currentQueryResult = null;
message = null;
try {
//Neues Statement erstellen
Statement statement = connection.createStatement();
//SQL Anweisung an die DB schicken.
if (statement.execute(pSQLStatement)) { //Fall 1: Es gibt ein Ergebnis
//Resultset auslesen
ResultSet resultset = statement.getResultSet();
//Spaltenanzahl ermitteln
int columnCount = resultset.getMetaData().getColumnCount();
//Spaltennamen und Spaltentypen in Felder uebertragen
String[] resultColumnNames = new String[columnCount];
String[] resultColumnTypes = new String[columnCount];
for (int i = 0; i < columnCount; i++){
resultColumnNames[i] = resultset.getMetaData().getColumnLabel(i+1);
resultColumnTypes[i] = resultset.getMetaData().getColumnTypeName(i+1);
}
//Queue fuer die Zeilen der Ergebnistabelle erstellen
Queue<String[]> rows = new Queue<String[]>();
//Daten in Queue uebertragen und Zeilen zaehlen
int rowCount = 0;
while (resultset.next()){
String[] resultrow = new String[columnCount];
for (int s = 0; s < columnCount; s++){
resultrow[s] = resultset.getString(s+1);
}
rows.enqueue(resultrow);
rowCount = rowCount + 1;
}
//Ergebnisfeld erstellen und Zeilen aus Queue uebertragen
String[][] resultData = new String[rowCount][columnCount];
int j = 0;
while (!rows.isEmpty()){
resultData[j] = rows.front();
rows.dequeue();
j = j + 1;
}
//Statement schließen und Ergebnisobjekt erstellen
statement.close();
currentQueryResult = new QueryResult(resultData, resultColumnNames, resultColumnTypes);
} else { //Fall 2: Es gibt kein Ergebnis.
//Statement ohne Ergebnisobjekt schliessen
statement.close();
}
} catch (Exception e) {
//Fehlermeldung speichern
message = e.getMessage();
}
}
/**
* Die Anfrage liefert das Ergebnis des letzten mit der Methode executeStatement an
* die Datenbank geschickten SQL-Befehls als Ob-jekt vom Typ QueryResult zurueck.
* Wurde bisher kein SQL-Befehl abgeschickt oder ergab der letzte Aufruf von
* executeStatement keine Ergebnismenge (z.B. bei einem INSERT-Befehl oder einem
* Syntaxfehler), so wird null geliefert.
*/
public QueryResult getCurrentQueryResult(){
return currentQueryResult;
}
/**
* Die Anfrage liefert null oder eine Fehlermeldung, die sich jeweils auf die letzte zuvor ausgefuehrte
* Datenbankoperation bezieht.
*/
public String getErrorMessage(){
return message;
}
/**
* Die Datenbankverbindung wird geschlossen.
*/
public void close(){
try{
connection.close();
} catch (Exception e) {
message = e.getMessage();
}
}
}

345
List.java Normal file
View File

@ -0,0 +1,345 @@
/**
* <p>
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
* </p>
* <p>
* Generische Klasse List<ContentType>
* </p>
* <p>
* Objekt der generischen Klasse List verwalten beliebig viele linear
* angeordnete Objekte vom Typ ContentType. Auf hoechstens ein Listenobjekt,
* aktuellesObjekt genannt, kann jeweils zugegriffen werden.<br />
* Wenn eine Liste leer ist, vollstaendig durchlaufen wurde oder das aktuelle
* Objekt am Ende der Liste geloescht wurde, gibt es kein aktuelles Objekt.<br />
* Das erste oder das letzte Objekt einer Liste koennen durch einen Auftrag zum
* aktuellen Objekt gemacht werden. Ausserdem kann das dem aktuellen Objekt
* folgende Listenobjekt zum neuen aktuellen Objekt werden. <br />
* Das aktuelle Objekt kann gelesen, veraendert oder geloescht werden. Ausserdem
* kann vor dem aktuellen Objekt ein Listenobjekt eingefuegt werden.
* </p>
*
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
* @version Generisch_06 2015-10-25
*/
public class List<ContentType> {
/* --------- Anfang der privaten inneren Klasse -------------- */
private class ListNode {
private ContentType contentObject;
private ListNode next;
/**
* Ein neues Objekt wird erschaffen. Der Verweis ist leer.
*
* @param pContent das Inhaltsobjekt vom Typ ContentType
*/
private ListNode(ContentType pContent) {
contentObject = pContent;
next = null;
}
/**
* Der Inhalt des Knotens wird zurueckgeliefert.
*
* @return das Inhaltsobjekt des Knotens
*/
public ContentType getContentObject() {
return contentObject;
}
/**
* Der Inhalt dieses Kontens wird gesetzt.
*
* @param pContent das Inhaltsobjekt vom Typ ContentType
*/
public void setContentObject(ContentType pContent) {
contentObject = pContent;
}
/**
* Der Nachfolgeknoten wird zurueckgeliefert.
*
* @return das Objekt, auf das der aktuelle Verweis zeigt
*/
public ListNode getNextNode() {
return this.next;
}
/**
* Der Verweis wird auf das Objekt, das als Parameter uebergeben
* wird, gesetzt.
*
* @param pNext der Nachfolger des Knotens
*/
public void setNextNode(ListNode pNext) {
this.next = pNext;
}
}
/* ----------- Ende der privaten inneren Klasse -------------- */
// erstes Element der Liste
ListNode first;
// letztes Element der Liste
ListNode last;
// aktuelles Element der Liste
ListNode current;
/**
* Eine leere Liste wird erzeugt.
*/
public List() {
first = null;
last = null;
current = null;
}
/**
* Die Anfrage liefert den Wert true, wenn die Liste keine Objekte enthaelt,
* sonst liefert sie den Wert false.
*
* @return true, wenn die Liste leer ist, sonst false
*/
public boolean isEmpty() {
// Die Liste ist leer, wenn es kein erstes Element gibt.
return first == null;
}
/**
* Die Anfrage liefert den Wert true, wenn es ein aktuelles Objekt gibt,
* sonst liefert sie den Wert false.
*
* @return true, falls Zugriff moeglich, sonst false
*/
public boolean hasAccess() {
// Es gibt keinen Zugriff, wenn current auf kein Element verweist.
return current != null;
}
/**
* Falls die Liste nicht leer ist, es ein aktuelles Objekt gibt und dieses
* nicht das letzte Objekt der Liste ist, wird das dem aktuellen Objekt in
* der Liste folgende Objekt zum aktuellen Objekt, andernfalls gibt es nach
* Ausfuehrung des Auftrags kein aktuelles Objekt, d.h. hasAccess() liefert
* den Wert false.
*/
public void next() {
if (this.hasAccess()) {
current = current.getNextNode();
}
}
/**
* Falls die Liste nicht leer ist, wird das erste Objekt der Liste aktuelles
* Objekt. Ist die Liste leer, geschieht nichts.
*/
public void toFirst() {
if (!isEmpty()) {
current = first;
}
}
/**
* Falls die Liste nicht leer ist, wird das letzte Objekt der Liste
* aktuelles Objekt. Ist die Liste leer, geschieht nichts.
*/
public void toLast() {
if (!isEmpty()) {
current = last;
}
}
/**
* Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das
* aktuelle Objekt zurueckgegeben, andernfalls (hasAccess() == false) gibt
* die Anfrage den Wert null zurueck.
*
* @return das aktuelle Objekt (vom Typ ContentType) oder null, wenn es
* kein aktuelles Objekt gibt
*/
public ContentType getContent() {
if (this.hasAccess()) {
return current.getContentObject();
} else {
return null;
}
}
/**
* Falls es ein aktuelles Objekt gibt (hasAccess() == true) und pContent
* ungleich null ist, wird das aktuelle Objekt durch pContent ersetzt. Sonst
* geschieht nichts.
*
* @param pContent
* das zu schreibende Objekt vom Typ ContentType
*/
public void setContent(ContentType pContent) {
// Nichts tun, wenn es keinen Inhalt oder kein aktuelles Element gibt.
if (pContent != null && this.hasAccess()) {
current.setContentObject(pContent);
}
}
/**
* Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird ein neues
* Objekt vor dem aktuellen Objekt in die Liste eingefuegt. Das aktuelle
* Objekt bleibt unveraendert. <br />
* Wenn die Liste leer ist, wird pContent in die Liste eingefuegt und es
* gibt weiterhin kein aktuelles Objekt (hasAccess() == false). <br />
* Falls es kein aktuelles Objekt gibt (hasAccess() == false) und die Liste
* nicht leer ist oder pContent gleich null ist, geschieht nichts.
*
* @param pContent
* das einzufuegende Objekt vom Typ ContentType
*/
public void insert(ContentType pContent) {
if (pContent != null) { // Nichts tun, wenn es keinen Inhalt gibt.
if (this.hasAccess()) { // Fall: Es gibt ein aktuelles Element.
// Neuen Knoten erstellen.
ListNode newNode = new ListNode(pContent);
if (current != first) { // Fall: Nicht an erster Stelle einfuegen.
ListNode previous = this.getPrevious(current);
newNode.setNextNode(previous.getNextNode());
previous.setNextNode(newNode);
} else { // Fall: An erster Stelle einfuegen.
newNode.setNextNode(first);
first = newNode;
}
} else { //Fall: Es gibt kein aktuelles Element.
if (this.isEmpty()) { // Fall: In leere Liste einfuegen.
// Neuen Knoten erstellen.
ListNode newNode = new ListNode(pContent);
first = newNode;
last = newNode;
}
}
}
}
/**
* Falls pContent gleich null ist, geschieht nichts.<br />
* Ansonsten wird ein neues Objekt pContent am Ende der Liste eingefuegt.
* Das aktuelle Objekt bleibt unveraendert. <br />
* Wenn die Liste leer ist, wird das Objekt pContent in die Liste eingefuegt
* und es gibt weiterhin kein aktuelles Objekt (hasAccess() == false).
*
* @param pContent
* das anzuhaengende Objekt vom Typ ContentType
*/
public void append(ContentType pContent) {
if (pContent != null) { // Nichts tun, wenn es keine Inhalt gibt.
if (this.isEmpty()) { // Fall: An leere Liste anfuegen.
this.insert(pContent);
} else { // Fall: An nicht-leere Liste anfuegen.
// Neuen Knoten erstellen.
ListNode newNode = new ListNode(pContent);
last.setNextNode(newNode);
last = newNode; // Letzten Knoten aktualisieren.
}
}
}
/**
* Falls es sich bei der Liste und pList um dasselbe Objekt handelt,
* pList null oder eine leere Liste ist, geschieht nichts.<br />
* Ansonsten wird die Liste pList an die aktuelle Liste angehaengt.
* Anschliessend wird pList eine leere Liste. Das aktuelle Objekt bleibt
* unveraendert. Insbesondere bleibt hasAccess identisch.
*
* @param pList
* die am Ende anzuhaengende Liste vom Typ List<ContentType>
*/
public void concat(List<ContentType> pList) {
if (pList != this && pList != null && !pList.isEmpty()) { // Nichts tun,
// wenn pList und this identisch, pList leer oder nicht existent.
if (this.isEmpty()) { // Fall: An leere Liste anfuegen.
this.first = pList.first;
this.last = pList.last;
} else { // Fall: An nicht-leere Liste anfuegen.
this.last.setNextNode(pList.first);
this.last = pList.last;
}
// Liste pList loeschen.
pList.first = null;
pList.last = null;
pList.current = null;
}
}
/**
* Wenn die Liste leer ist oder es kein aktuelles Objekt gibt (hasAccess()
* == false), geschieht nichts.<br />
* Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das
* aktuelle Objekt geloescht und das Objekt hinter dem geloeschten Objekt
* wird zum aktuellen Objekt. <br />
* Wird das Objekt, das am Ende der Liste steht, geloescht, gibt es kein
* aktuelles Objekt mehr.
*/
public void remove() {
// Nichts tun, wenn es kein aktuelle Element gibt oder die Liste leer ist.
if (this.hasAccess() && !this.isEmpty()) {
if (current == first) {
first = first.getNextNode();
} else {
ListNode previous = this.getPrevious(current);
if (current == last) {
last = previous;
}
previous.setNextNode(current.getNextNode());
}
ListNode temp = current.getNextNode();
current.setContentObject(null);
current.setNextNode(null);
current = temp;
//Beim loeschen des letzten Elements last auf null setzen.
if (this.isEmpty()) {
last = null;
}
}
}
/**
* Liefert den Vorgaengerknoten des Knotens pNode. Ist die Liste leer, pNode
* == null, pNode nicht in der Liste oder pNode der erste Knoten der Liste,
* wird null zurueckgegeben.
*
* @param pNode
* der Knoten, dessen Vorgaenger zurueckgegeben werden soll
* @return der Vorgaenger des Knotens pNode oder null, falls die Liste leer ist,
* pNode == null ist, pNode nicht in der Liste ist oder pNode der erste Knoten
* der Liste ist
*/
private ListNode getPrevious(ListNode pNode) {
if (pNode != null && pNode != first && !this.isEmpty()) {
ListNode temp = first;
while (temp != null && temp.getNextNode() != pNode) {
temp = temp.getNextNode();
}
return temp;
} else {
return null;
}
}
}

76
QueryResult.java Normal file
View File

@ -0,0 +1,76 @@
/**
* <p>
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
* </p>
* <p>
* Klasse QueryResult
* </p>
* <p>
* Ein Objekt der Klasse QueryResult stellt die Ergebnistabelle einer Datenbankanfrage mit Hilfe
* der Klasse DatabaseConnector dar. Objekte dieser Klasse werden nur von der Klasse DatabaseConnector erstellt.
* Die Klasse verfuegt ueber keinen oeffentlichen Konstruktor.
* </p>
*
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
* @version 2015-01-31
*/
public class QueryResult{
private String[][] data;
private String[] columnNames;
private String[] columnTypes;
/**
* Paketinterner Konstruktor.
*/
QueryResult(String[][] pData, String[] pColumnNames, String[] pColumnTypes){
data = pData;
columnNames = pColumnNames;
columnTypes = pColumnTypes;
}
/**
* Die Anfrage liefert die Eintraege der Ergebnistabelle als zweidimensionales Feld
* vom Typ String. Der erste Index des Feldes stellt die Zeile und der zweite die
* Spalte dar (d.h. Object[zeile][spalte]).
*/
public String[][] getData(){
return data;
}
/**
* Die Anfrage liefert die Bezeichner der Spalten der Ergebnistabelle als Feld vom
* Typ String zurueck.
*/
public String[] getColumnNames(){
return columnNames;
}
/**
* Die Anfrage liefert die Typenbezeichnung der Spalten der Ergebnistabelle als Feld
* vom Typ String zurueck. Die Bezeichnungen entsprechen den Angaben in der MySQL-Datenbank.
*/
public String[] getColumnTypes(){
return columnTypes;
}
/**
* Die Anfrage liefert die Anzahl der Zeilen der Ergebnistabelle als Integer.
*/
public int getRowCount(){
if (data != null )
return data.length;
else
return 0;
}
/**
* Die Anfrage liefert die Anzahl der Spalten der Ergebnistabelle als Integer.
*/
public int getColumnCount(){
if (data != null && data.length > 0 && data[0] != null)
return data[0].length;
else
return 0;
}
}

142
Queue.java Normal file
View File

@ -0,0 +1,142 @@
/**
* <p>
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
* </p>
* <p>
* Generische Klasse Queue<ContentType>
* </p>
* <p>
* Objekte der generischen Klasse Queue (Warteschlange) verwalten beliebige
* Objekte vom Typ ContentType nach dem First-In-First-Out-Prinzip, d.h., das
* zuerst abgelegte Objekt wird als erstes wieder entnommen. Alle Methoden haben
* eine konstante Laufzeit, unabhaengig von der Anzahl der verwalteten Objekte.
* </p>
*
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
* @version Generisch_02 2014-02-21
*/
public class Queue<ContentType> {
/* --------- Anfang der privaten inneren Klasse -------------- */
private class QueueNode {
private ContentType content = null;
private QueueNode nextNode = null;
/**
* Ein neues Objekt vom Typ QueueNode<ContentType> wird erschaffen.
* Der Inhalt wird per Parameter gesetzt. Der Verweis ist leer.
*
* @param pContent das Inhaltselement des Knotens vom Typ ContentType
*/
public QueueNode(ContentType pContent) {
content = pContent;
nextNode = null;
}
/**
* Der Verweis wird auf das Objekt, das als Parameter uebergeben wird,
* gesetzt.
*
* @param pNext der Nachfolger des Knotens
*/
public void setNext(QueueNode pNext) {
nextNode = pNext;
}
/**
* Liefert das naechste Element des aktuellen Knotens.
*
* @return das Objekt vom Typ QueueNode, auf das der aktuelle Verweis zeigt
*/
public QueueNode getNext() {
return nextNode;
}
/**
* Liefert das Inhaltsobjekt des Knotens vom Typ ContentType.
*
* @return das Inhaltsobjekt des Knotens
*/
public ContentType getContent() {
return content;
}
}
/* ----------- Ende der privaten inneren Klasse -------------- */
private QueueNode head;
private QueueNode tail;
/**
* Eine leere Schlange wird erzeugt.
* Objekte, die in dieser Schlange verwaltet werden, muessen vom Typ
* ContentType sein.
*/
public Queue() {
head = null;
tail = null;
}
/**
* Die Anfrage liefert den Wert true, wenn die Schlange keine Objekte enthaelt,
* sonst liefert sie den Wert false.
*
* @return true, falls die Schlange leer ist, sonst false
*/
public boolean isEmpty() {
return head == null;
}
/**
* Das Objekt pContentType wird an die Schlange angehaengt.
* Falls pContentType gleich null ist, bleibt die Schlange unveraendert.
*
* @param pContent
* das anzuhaengende Objekt vom Typ ContentType
*/
public void enqueue(ContentType pContent) {
if (pContent != null) {
QueueNode newNode = new QueueNode(pContent);
if (this.isEmpty()) {
head = newNode;
tail = newNode;
} else {
tail.setNext(newNode);
tail = newNode;
}
}
}
/**
* Das erste Objekt wird aus der Schlange entfernt.
* Falls die Schlange leer ist, wird sie nicht veraendert.
*/
public void dequeue() {
if (!this.isEmpty()) {
head = head.getNext();
if (this.isEmpty()) {
head = null;
tail = null;
}
}
}
/**
* Die Anfrage liefert das erste Objekt der Schlange.
* Die Schlange bleibt unveraendert.
* Falls die Schlange leer ist, wird null zurueckgegeben.
*
* @return das erste Objekt der Schlange vom Typ ContentType oder null,
* falls die Schlange leer ist
*/
public ContentType front() {
if (this.isEmpty()) {
return null;
} else {
return head.getContent();
}
}
}

359
Server.java Normal file
View File

@ -0,0 +1,359 @@
/**
* <p>
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
* </p>
* <p>
* Klasse Server
* </p>
* <p>
* Objekte von Unterklassen der abstrakten Klasse Server ermoeglichen das
* Anbieten von Serverdiensten, so dass Clients Verbindungen zum Server mittels
* TCP/IP-Protokoll aufbauen koennen. Zur Vereinfachung finden Nachrichtenversand
* und -empfang zeilenweise statt, d. h., beim Senden einer Zeichenkette wird ein
* Zeilentrenner ergaenzt und beim Empfang wird dieser entfernt.
* Verbindungsannahme, Nachrichtenempfang und Verbindungsende geschehen
* nebenlaeufig. Auf diese Ereignisse muss durch Ueberschreiben der entsprechenden
* Ereignisbehandlungsmethoden reagiert werden. Es findet nur eine rudimentaere
* Fehlerbehandlung statt, so dass z.B. Verbindungsabbrueche nicht zu einem
* Programmabbruch fuehren. Einmal unterbrochene oder getrennte Verbindungen
* koennen nicht reaktiviert werden.
* </p>
*
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
* @version 30.08.2016
*/
import java.net.*;
import java.io.*;
public abstract class Server
{
private NewConnectionHandler connectionHandler;
private List<ClientMessageHandler> messageHandlers;
private class NewConnectionHandler extends Thread
{
private ServerSocket serverSocket;
private boolean active;
public NewConnectionHandler(int pPort)
{
try
{
serverSocket = new ServerSocket(pPort);
start();
active = true;
}
catch (Exception e)
{
serverSocket = null;
active = false;
}
}
public void run()
{
while (active)
{
try
{
//Warten auf Verbdinungsversuch durch Client:
Socket clientSocket = serverSocket.accept();
// Eingehende Nachrichten vom neu verbundenen Client werden
// in einem eigenen Thread empfangen:
addNewClientMessageHandler(clientSocket);
processNewConnection(clientSocket.getInetAddress().getHostAddress(),clientSocket.getPort());
}
catch (IOException e)
{
/*
* Kann keine Verbindung zum anfragenden Client aufgebaut werden,
* geschieht nichts.
*/
}
}
}
public void close()
{
active = false;
if(serverSocket != null)
try
{
serverSocket.close();
}
catch (IOException e)
{
/*
* Befindet sich der ServerSocket im accept()-Wartezustand oder wurde
* er bereits geschlossen, geschieht nichts.
*/
}
}
}
private class ClientMessageHandler extends Thread
{
private ClientSocketWrapper socketWrapper;
private boolean active;
private class ClientSocketWrapper
{
private Socket clientSocket;
private BufferedReader fromClient;
private PrintWriter toClient;
public ClientSocketWrapper(Socket pSocket)
{
try
{
clientSocket = pSocket;
toClient = new PrintWriter(clientSocket.getOutputStream(), true);
fromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
}
catch (IOException e)
{
clientSocket = null;
toClient = null;
fromClient = null;
}
}
public String receive()
{
if(fromClient != null)
try
{
return fromClient.readLine();
}
catch (IOException e)
{
}
return(null);
}
public void send(String pMessage)
{
if(toClient != null)
{
toClient.println(pMessage);
}
}
public String getClientIP()
{
if(clientSocket != null)
return(clientSocket.getInetAddress().getHostAddress());
else
return(null); //Gemaess Java-API Rueckgabe bei nicht-verbundenen Sockets
}
public int getClientPort()
{
if(clientSocket != null)
return(clientSocket.getPort());
else
return(0); //Gemaess Java-API Rueckgabe bei nicht-verbundenen Sockets
}
public void close()
{
if(clientSocket != null)
try
{
clientSocket.close();
}
catch (IOException e)
{
/*
* Falls eine Verbindung getrennt werden soll, deren Endpunkt
* nicht mehr existiert bzw. ihrerseits bereits beendet worden ist,
* geschieht nichts.
*/
}
}
}
private ClientMessageHandler(Socket pClientSocket)
{
socketWrapper = new ClientSocketWrapper(pClientSocket);
if(pClientSocket!=null)
{
start();
active = true;
}
else
{
active = false;
}
}
public void run()
{
String message = null;
while (active)
{
message = socketWrapper.receive();
if (message != null)
processMessage(socketWrapper.getClientIP(), socketWrapper.getClientPort(), message);
else
{
ClientMessageHandler aMessageHandler = findClientMessageHandler(socketWrapper.getClientIP(), socketWrapper.getClientPort());
if (aMessageHandler != null)
{
aMessageHandler.close();
removeClientMessageHandler(aMessageHandler);
processClosingConnection(socketWrapper.getClientIP(), socketWrapper.getClientPort());
}
}
}
}
public void send(String pMessage)
{
if(active)
socketWrapper.send(pMessage);
}
public void close()
{
if(active)
{
active=false;
socketWrapper.close();
}
}
public String getClientIP()
{
return(socketWrapper.getClientIP());
}
public int getClientPort()
{
return(socketWrapper.getClientPort());
}
}
public Server(int pPort)
{
connectionHandler = new NewConnectionHandler(pPort);
messageHandlers = new List<ClientMessageHandler>();
}
public boolean isOpen()
{
return(connectionHandler.active);
}
public boolean isConnectedTo(String pClientIP, int pClientPort)
{
ClientMessageHandler aMessageHandler = findClientMessageHandler(pClientIP, pClientPort);
if (aMessageHandler != null)
return(aMessageHandler.active);
else
return(false);
}
public void send(String pClientIP, int pClientPort, String pMessage)
{
ClientMessageHandler aMessageHandler = this.findClientMessageHandler(pClientIP, pClientPort);
if (aMessageHandler != null)
aMessageHandler.send(pMessage);
}
public void sendToAll(String pMessage)
{
synchronized(messageHandlers)
{
messageHandlers.toFirst();
while (messageHandlers.hasAccess())
{
messageHandlers.getContent().send(pMessage);
messageHandlers.next();
}
}
}
public void closeConnection(String pClientIP, int pClientPort)
{
ClientMessageHandler aMessageHandler = findClientMessageHandler(pClientIP, pClientPort);
if (aMessageHandler != null)
{
processClosingConnection(pClientIP, pClientPort);
aMessageHandler.close();
removeClientMessageHandler(aMessageHandler);
}
}
public void close()
{
connectionHandler.close();
synchronized(messageHandlers)
{
ClientMessageHandler aMessageHandler;
messageHandlers.toFirst();
while (messageHandlers.hasAccess())
{
aMessageHandler = messageHandlers.getContent();
processClosingConnection(aMessageHandler.getClientIP(), aMessageHandler.getClientPort());
aMessageHandler.close();
messageHandlers.remove();
}
}
}
public abstract void processNewConnection(String pClientIP, int pClientPort);
public abstract void processMessage(String pClientIP, int pClientPort, String pMessage);
public abstract void processClosingConnection(String pClientIP, int pClientPort);
private void addNewClientMessageHandler(Socket pClientSocket)
{
synchronized(messageHandlers)
{
messageHandlers.append(new Server.ClientMessageHandler(pClientSocket));
}
}
private void removeClientMessageHandler(ClientMessageHandler pClientMessageHandler)
{
synchronized(messageHandlers)
{
messageHandlers.toFirst();
while (messageHandlers.hasAccess())
{
if (pClientMessageHandler == messageHandlers.getContent())
{
messageHandlers.remove();
return;
}
else
messageHandlers.next();
}
}
}
private ClientMessageHandler findClientMessageHandler(String pClientIP, int pClientPort)
{
synchronized(messageHandlers)
{
ClientMessageHandler aMessageHandler;
messageHandlers.toFirst();
while (messageHandlers.hasAccess())
{
aMessageHandler = messageHandlers.getContent();
if (aMessageHandler.getClientIP().equals(pClientIP) && aMessageHandler.getClientPort() == pClientPort)
return (aMessageHandler);
messageHandlers.next();
}
return (null);
}
}
}

91
package.bluej Executable file
View File

@ -0,0 +1,91 @@
#BlueJ package file
dependency1.from=DecisionNode
dependency1.to=Dataset
dependency1.type=UsesDependency
dependency2.from=Classification
dependency2.to=Dataset
dependency2.type=UsesDependency
dependency3.from=Decision
dependency3.to=Dataset
dependency3.type=UsesDependency
dependency4.from=DecisionTreeBuilder
dependency4.to=BinaryTree
dependency4.type=UsesDependency
dependency5.from=DecisionTreeBuilder
dependency5.to=DecisionNode
dependency5.type=UsesDependency
dependency6.from=DecisionTreeBuilder
dependency6.to=Classification
dependency6.type=UsesDependency
dependency7.from=DecisionTreeBuilder
dependency7.to=Decision
dependency7.type=UsesDependency
dependency8.from=DecisionTreeBuilder
dependency8.to=Dataset
dependency8.type=UsesDependency
editor.fx.0.height=722
editor.fx.0.width=800
editor.fx.0.x=388
editor.fx.0.y=50
objectbench.height=66
objectbench.width=1201
package.divider.horizontal=0.6
package.divider.vertical=0.8983286908077994
package.editor.height=622
package.editor.width=1078
package.editor.x=39
package.editor.y=24
package.frame.height=776
package.frame.width=1241
package.numDependencies=8
package.numTargets=6
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.height=58
readme.name=@README
readme.width=47
readme.x=10
readme.y=10
target1.height=40
target1.name=Classification
target1.showInterface=false
target1.type=ClassTarget
target1.width=210
target1.x=40
target1.y=460
target2.height=50
target2.name=Decision
target2.showInterface=false
target2.type=ClassTarget
target2.width=190
target2.x=300
target2.y=460
target3.height=120
target3.name=BinaryTree
target3.showInterface=false
target3.type=ClassTarget
target3.width=440
target3.x=890
target3.y=250
target4.height=40
target4.name=Dataset
target4.showInterface=false
target4.type=ClassTarget
target4.width=270
target4.x=430
target4.y=30
target5.height=50
target5.name=DecisionNode
target5.showInterface=false
target5.type=AbstractTarget
target5.width=110
target5.x=210
target5.y=290
target6.height=60
target6.name=DecisionTreeBuilder
target6.showInterface=false
target6.type=ClassTarget
target6.width=270
target6.x=580
target6.y=230

BIN
wordle.db Normal file

Binary file not shown.