commit 53beb78043b9eb4a0f81aaf7464b3a82ee9c1b5c Author: J. Neugebauer Date: Sun Feb 6 21:41:45 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..173985e --- /dev/null +++ b/.gitignore @@ -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 diff --git a/DatabaseConnector.java b/DatabaseConnector.java new file mode 100644 index 0000000..3e48e1d --- /dev/null +++ b/DatabaseConnector.java @@ -0,0 +1,148 @@ +import java.sql.*; + +/** + *

+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018 + *

+ *

+ * Klasse DatabaseConnector + *

+ *

+ * 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. + *

+ * + * @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 rows = new Queue(); + + //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(); + } + } + +} diff --git a/List.java b/List.java new file mode 100644 index 0000000..8c82b92 --- /dev/null +++ b/List.java @@ -0,0 +1,345 @@ + /** + *

+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018 + *

+ *

+ * Generische Klasse List + *

+ *

+ * Objekt der generischen Klasse List verwalten beliebig viele linear + * angeordnete Objekte vom Typ ContentType. Auf hoechstens ein Listenobjekt, + * aktuellesObjekt genannt, kann jeweils zugegriffen werden.
+ * Wenn eine Liste leer ist, vollstaendig durchlaufen wurde oder das aktuelle + * Objekt am Ende der Liste geloescht wurde, gibt es kein aktuelles Objekt.
+ * 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.
+ * Das aktuelle Objekt kann gelesen, veraendert oder geloescht werden. Ausserdem + * kann vor dem aktuellen Objekt ein Listenobjekt eingefuegt werden. + *

+ * + * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule + * @version Generisch_06 2015-10-25 + */ +public class List { + + /* --------- 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.
+ * Wenn die Liste leer ist, wird pContent in die Liste eingefuegt und es + * gibt weiterhin kein aktuelles Objekt (hasAccess() == false).
+ * 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.
+ * Ansonsten wird ein neues Objekt pContent am Ende der Liste eingefuegt. + * Das aktuelle Objekt bleibt unveraendert.
+ * 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.
+ * 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 + */ + public void concat(List 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.
+ * Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das + * aktuelle Objekt geloescht und das Objekt hinter dem geloeschten Objekt + * wird zum aktuellen Objekt.
+ * 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; + } + } + +} diff --git a/QueryResult.java b/QueryResult.java new file mode 100644 index 0000000..cb35dc4 --- /dev/null +++ b/QueryResult.java @@ -0,0 +1,76 @@ +/** + *

+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018 + *

+ *

+ * Klasse QueryResult + *

+ *

+ * 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. + *

+ * + * @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; + } + +} \ No newline at end of file diff --git a/Queue.java b/Queue.java new file mode 100644 index 0000000..e7835c0 --- /dev/null +++ b/Queue.java @@ -0,0 +1,142 @@ +/** + *

+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018 + *

+ *

+ * Generische Klasse Queue + *

+ *

+ * 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. + *

+ * + * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule + * @version Generisch_02 2014-02-21 + */ +public class Queue { + + /* --------- Anfang der privaten inneren Klasse -------------- */ + + private class QueueNode { + + private ContentType content = null; + private QueueNode nextNode = null; + + /** + * Ein neues Objekt vom Typ QueueNode 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(); + } + } +} diff --git a/Server.java b/Server.java new file mode 100644 index 0000000..710ecc0 --- /dev/null +++ b/Server.java @@ -0,0 +1,359 @@ +/** + *

+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018 + *

+ *

+ * Klasse Server + *

+ *

+ * 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. + *

+ * + * @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 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(); + } + + 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); + } + } + +} \ No newline at end of file diff --git a/package.bluej b/package.bluej new file mode 100755 index 0000000..460a419 --- /dev/null +++ b/package.bluej @@ -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 diff --git a/wordle.db b/wordle.db new file mode 100644 index 0000000..051609c Binary files /dev/null and b/wordle.db differ