/* ** 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; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.LinkedList; import java.util.ListIterator; import java.util.Observer; import filius.Main; import filius.rahmenprogramm.Information; import filius.software.system.InternetKnotenBetriebssystem; /** * Die Klasse Anwendung ist die Oberklasse aller Anwendungen, die auf einem * Rechner installiert werden koennen. Als beobachtetes Objekt implementiert * diese Klasse eine Komponente des Beobachtermusters mit Hilfe der Klasse * AnwendungObservable. Es werden die Standardkomponenten des JDK verwendet. * * @see java.util.Observable * @see filius.software.AnwendungObservable */ public abstract class Anwendung extends Thread { /** Bezeichnung fuer die Anwendung */ private String anwendungsName; /** * Ein Puffer fuer eingehende Kommandos. In dem Puffer werden Objekt-Arrays * aus zwei Elementen gespeichert. Das erste Element ist ein String, der die * aufzurufende Methode bestimmt. Das zweite Element ist ein Objekt-Array * mit den Parametern fuer den Methodenaufruf. */ private LinkedList<Object[]> kommandos = new LinkedList<Object[]>(); /** * Das Betriebssystem des Rechners/Vermittlungsrechner, auf dem die * Anwendung ausgefuehrt wird. */ private InternetKnotenBetriebssystem betriebssystem; /** Beobachter der Anwendung */ private AnwendungObservable observable = new AnwendungObservable(); /** Dieses Attribut zeigt an, ob der Thread laeuft. */ protected boolean running = false; /** * Der Konstruktur bewirkt eine Meldung auf der Standardausgabe, dass die * Anwendung erzeugt wurde. */ public Anwendung() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), constr: Anwendung()"); ListIterator<HashMap<String, String>> it; boolean erfolg = false; HashMap<String, String> tmpMap; try { it = Information.getInformation().ladeProgrammListe() .listIterator(); while (it.hasNext() && !erfolg) { tmpMap = (HashMap<String, String>) it.next(); if (this.getClass().getCanonicalName().equals( (String) tmpMap.get("Klasse"))) { this .setzeAnwendungsName(tmpMap.get("Anwendung") .toString()); } } } catch (Exception e) { e.printStackTrace(Main.debug); } } /** * Methode fuer das Beobachtermuster: Hinzufuegen eines weiteren * Beobachters. * * @param beobachter */ public void hinzuBeobachter(Observer beobachter) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), hinzuBeobachter("+beobachter+")"); observable.addObserver(beobachter); } /** Methode zur Benachrichtigung der Beobachter. */ public void benachrichtigeBeobachter(Object daten) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), benachrichtigeBeobachter("+daten+")"); observable.notifyObservers(daten); } /** Methode zur Benachrichtigung der Beobachter. */ public void benachrichtigeBeobachter() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), benachrichtigeBeobachter()"); observable.notifyObservers(); } /** * Methode zum Starten des Threads beim Wechsel vom Entwurfs- in den * Aktionsmodus. */ public void starten() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), starten()"); running = true; synchronized (kommandos) { kommandos.clear(); } if (getState().equals(State.NEW)) { start(); } else { synchronized (this) { notifyAll(); } } } /** * Methode zum Anhalten des Threads. */ public void beenden() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), beenden()"); running = false; if (kommandos != null) { synchronized (kommandos) { kommandos.clear(); kommandos.notifyAll(); } } } /** * Methode zur Uebergabe von auszufuehrenden Kommandos. Die Uebergebenen * Methodenaufrufe werden in dem Thread ausgefuehrt, der von dieser Klasse * implementiert wird. Damit wird der aufrufende Thread nicht blockiert. Die * Verwendung dieser Moeglichkeit fuer Methodenaufrufe ist also zur * <b>Ausfuehrung blockierender Methoden</b> wichtig. * * @param methode * Der Bezeichner der auszufuehrenden Methode * @param args * die Parameter der Methode */ protected void ausfuehren(String methode, Object[] args) { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), ausfuehren("+methode+","+args+")"); Object[] aufruf; aufruf = new Object[2]; aufruf[0] = methode; aufruf[1] = args; synchronized (kommandos) { //Main.debug.println(getClass() + "\tAufruf von Methode '" + methode //+ "' wurde in Warteschlange 'kommandos' eingefuegt!" //+ "\n\tThread ist im Zustand: " + this.getState()); kommandos.addLast(aufruf); /// debug: //Main.debug.print("\tDEBUG ('ausfuehren' in "+this.hashCode()+", T"+this.getId()+"): kommandos=["); //for (int i=0; i<kommandos.size(); i++) { //Main.debug.print((String) (kommandos.get(i)[0])); //Main.debug.print(","); //} //Main.debug.println("]"); /////// kommandos.notifyAll(); } } /** * Hier wird der Puffer kommandos ueberwacht und wenn dort ein * Methodenaufruf vorliegt wird diese Methode aufgerufen. */ public void run() { Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (Anwendung), run()"); Class<?>[] argumentKlassen; Class<?> klasse; Method method; String methodenName; Object[] args; Object[] aufruf; while (true) { if (running) { aufruf = null; synchronized (kommandos) { // first block, then check size! (otherwise: prone to race conditions) if (kommandos.size() < 1) { try { /// debug: //Main.debug.print("\tDEBUG ('run' in "+this.hashCode()+", T"+this.getId()+"): kommandos=["); //for (int i=0; i<kommandos.size(); i++) { //Main.debug.print((String) (kommandos.get(i)[0])); //Main.debug.print(","); //} //Main.debug.println("]"); /////// kommandos.wait(); } catch (InterruptedException e) { } //Main.debug.println(getClass() + " run()" //+ "\n\tThread wurde aufgeweckt."); } else { aufruf = (Object[]) kommandos.removeFirst(); } } if (aufruf != null) { methodenName = aufruf[0].toString(); args = (Object[]) aufruf[1]; if (args != null) { argumentKlassen = new Class[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] != null) argumentKlassen[i] = args[i].getClass(); // else //Main.debug.println(getClass() + "\n\tArgumentklasse fehlerhaft (Argument " //+ i + ")" + "\n\tMethode: " //+ methodenName); } } else { argumentKlassen = null; } klasse = getClass(); // go upwards in inheritance hierarchy until the class was found containing // the desired method, i.e., exceptions are rather harmless here while (klasse != null) { try { method = klasse.getDeclaredMethod(methodenName, argumentKlassen); //Main.debug.println(getClass() + ", run(): \n\tMethode '" //+ methodenName + "' gefunden in Klasse " //+ klasse.toString() //+ "\n\t--> " //+ "Aufruf der Methode '" //+ method.toString() + "'"); method.invoke(this, args); klasse = null; } catch (NoSuchMethodException e) { //Main.debug.println(getClass() + ", run(): \n\tMethode '" //+ methodenName + "' nicht in Klasse " //+ klasse.toString() //+ "\n\t--> fahre fort mit Suche"); klasse = klasse.getSuperclass(); } catch (IllegalAccessException e) { e.printStackTrace(Main.debug); klasse = null; } catch (InvocationTargetException e) { e.getCause().printStackTrace(Main.debug); klasse = null; } } } } else { synchronized (this) { try { //Main.debug //.println(getClass() //+ "\n\tThread wurde beim Wechsel in Entwurfsmodus angehalten"); wait(); //Main.debug //.println(getClass() //+ "\n\tThread beim Wechsel in Aktionsmodus fortgesetzt"); } catch (InterruptedException e) { } } } } } /** Methode fuer den Zugriff auf den Anwendungsnamen */ public String holeAnwendungsName() { return anwendungsName; } /** Methode fuer den Zugriff auf den Anwendungsnamen */ public void setzeAnwendungsName(String anwendungsName) { this.anwendungsName = anwendungsName; } public void setAnwendungsName(String anwendungsName) { // method for downward compatibility; older versions of filius possibly // used this method, such that some saved scenarios depend on it! // ... or maybe only JAVA demands properties to be set by a "set" method! (required by XMLDecoder) setzeAnwendungsName(anwendungsName); } /** * Methode fuer den Zugriff auf das Betriebssystem, auf dem diese Anwendung * laeuft. * * @param bs */ public void setSystemSoftware(InternetKnotenBetriebssystem bs) { betriebssystem = bs; } /** * Methode fuer den Zugriff auf das Betriebssystem, auf dem diese Anwendung * laeuft. * * @param bs */ public InternetKnotenBetriebssystem getSystemSoftware() { return betriebssystem; } }