/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl <filius@c.fameibl.de> ** and Stefan Freischlad ** Filius is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 2 of the License, or ** (at your option) version 3. ** ** Filius is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ** PURPOSE. See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with Filius. If not, see <http://www.gnu.org/licenses/>. */ package filius.software.clientserver; import java.util.LinkedList; import java.util.ListIterator; import filius.Main; import filius.exception.ServerSocketException; import filius.rahmenprogramm.I18n; import filius.software.Anwendung; import filius.software.transportschicht.ServerSocket; import filius.software.transportschicht.Socket; import filius.software.transportschicht.SocketSchnittstelle; /** * Diese Klasse ist die Oberklasse fuer Serveranwendungen. Dazu wird ein * Server-Socket und Methoden zur Verbindungsherstellung zur Verfuegung * gestellt. Ausserdem wird eine Liste von Mitarbeiter-Threads, die die * Anwendungslogik implementieren bzw. die Verarbeitung eingehender * Verbindungsanfragen und Dienstnforderungen uebernehmen. */ public abstract class ServerAnwendung extends Anwendung implements I18n { /** Konstante: UDP oder TCP der Klasse TransportProtokoll */ protected int transportProtokoll; /** der Socket zur Annahme eingehender Verbindungsanfragen */ protected SocketSchnittstelle socket; /** Der TCP-Port, der auf eingehende Verbindungsanfragen wartet. */ protected int port = 55555; /** * Ob der Server aktiv ist, d. h. ob auf eingehende Verbindungsanfragen * gewartet wird. */ protected boolean aktiv = false; /** * Liste von Mitarbeitern, die die Bearbeitung der erstellten Verbindungen * vornehmen. */ protected LinkedList<ServerMitarbeiter> mitarbeiter; /** Konstruktor zur Initialisierung des verwendeten TransportProtokolls */ public ServerAnwendung(int transportProtokoll) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+", constr: ServerAnwendung("+transportProtokoll+")"); this.transportProtokoll = transportProtokoll; } /** * Methode fuer den Zugriff auf die Portnummer, auf der eingehende * Verbindungen angenommen werden */ public int getPort() { return port; } /** * Methode fuer den Zugriff auf die Portnummer, auf der eingehende * Verbindungen angenommen werden */ public void setPort(int port) { this.port = port; } /** * Methode zur Abfrage, ob der Server-Socket auf eingehende Verbindungen * wartet. */ public boolean isAktiv() { return aktiv; } /** * Zum aktivieren bzw. deaktivieren des Servers. Wenn der Server aktiv ist, * wartet er auf eingehende Verbindungsanfragen. Sonst ist der Port * geschlossen. * * @param flag */ public void setAktiv(boolean flag) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (ServerAnwendung), setAktiv("+flag+")"); aktiv = flag; //Main.debug.println(getClass() + "\n\taktiv = " + aktiv); if (getState().equals(State.WAITING)) { synchronized (this) { //Main.debug.println("\taufgeweckt"); notifyAll(); } } if (!flag) { if (socket != null) socket.schliessen(); socket = null; benachrichtigeBeobachter(messages .getString("sw_serveranwendung_msg1")); } else { benachrichtigeBeobachter(messages .getString("sw_serveranwendung_msg2")); } } /** * Methode zum Starten des Threads beim Wechsel vom Entwurfs- in den * Aktionsmodus. Hier wird die Liste der Mitarbeiter als leere Liste * erstellt und die starten()-Methode der Oberklasse zum Starten des Threads * aufgerufen. */ public void starten() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (ServerAnwendung), starten()"); super.starten(); mitarbeiter = new LinkedList<ServerMitarbeiter>(); ausfuehren("annehmenVerbindungen", null); } // return, whether this application can be used already public boolean isStarted() { return (socket != null); } /** * Methode zum Anhalten des Threads. Hier wird die beenden()-Methode der * Oberklasse aufgerufen und die Mitarbeiter-Threads sowie die * Socket-Schnittstelle werden beendet. */ public void beenden() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (ServerAnwendung), beenden()"); ListIterator it; super.beenden(); it = mitarbeiter.listIterator(); while (it.hasNext()) { ((ServerMitarbeiter) it.next()).beenden(); } if (socket != null) socket.beenden(); socket = null; } /** * Methode zum erzeugen eines neuen Mitarbeiters, wenn eine * Verbindungsanfrage eingetroffen ist. <b>Diese Methode muss von * Unterklassen ueberschrieben werden, um den Mitarbeiter mit der richtigen * Anwengungslogik zu erzeugen.</b> <br /> * In dieser Methode wird der Mitarbeiter erzeugt und der Liste der * Mitarbeiter hinzugefuegt. Das muss in den Unterklassen implementiert * werden. * * @param socket */ protected abstract void neuerMitarbeiter(Socket socket); /** * Methode zum entfernen eines Mitarbeiters, dessen Socket geschlossen und * der Thread beendet worden ist. <br /> * In dieser Methode wird der Mitarbeiter nur aus der Liste der verwalteten * Threads entfernt. * * @param thread * der nicht mehr aktive Mitarbeiter */ public void entferneMitarbeiter(ServerMitarbeiter thread) { mitarbeiter.remove(thread); } /** * Die Aufgabe des Threads der Server-Anwendung besteht darin, wenn der * Server aktiv ist, auf eingehende Verbindungsanforderungen zu warten. Wenn * eine Anforderung erfolgt, wird ein neuer Mitarbeiter mit der Methode * neuerMitarbeiter() erstellt, der die weitere Verarbeitung uebernimmt. */ public void annehmenVerbindungen() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (ServerAnwendung), annehmenVerbindungen()"); Socket transportSocket; while (running) { if (aktiv) { if (socket == null) { try { socket = new ServerSocket(getSystemSoftware(), port, transportProtokoll); } catch (ServerSocketException e) { e.printStackTrace(Main.debug); benachrichtigeBeobachter(messages.getString("sw_serveranwendung_msg3")); setAktiv(false); if (socket != null) socket.beenden(); socket = null; } } if (socket != null) { try { transportSocket = ((ServerSocket) socket).oeffnen(); if (transportSocket != null && transportSocket.holeZielIPAdresse() != null) { neuerMitarbeiter(transportSocket); benachrichtigeBeobachter(messages .getString("sw_serveranwendung_msg4") + " " + transportSocket.holeZielIPAdresse() + ":" + transportSocket.holeZielPort() + " " + messages .getString("sw_serveranwendung_msg5")); } } catch (Exception e) { benachrichtigeBeobachter(e.getMessage()); e.printStackTrace(Main.debug); } } } else { synchronized (this) { try { wait(); //Main.debug.println(getClass() //+ "\n\tThread fortgesetzt nach Aktivierung der Anwendung"); } catch (InterruptedException e) { } } } } //Main.debug.println(getClass() //+ "aktiven Zustand und damit Verbindungsannahme beendet"); } }