/*
** 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.dateiaustausch;
import java.util.LinkedList;
import filius.Main;
import filius.software.clientserver.ServerMitarbeiter;
import filius.software.system.Betriebssystem;
import filius.software.system.Datei;
import filius.software.transportschicht.Socket;
import filius.software.www.HTTPNachricht;
/**
* In dieser Klasse wird ein Thread implementiert, der eingehende Anfragen an
* eine Peer-to-Peer-Anwendung verarbeitet.
*/
public class PeerToPeerServerMitarbeiter extends ServerMitarbeiter {
private PeerToPeerAnwendung peerToPeerAnwendung;
/** die GUID der eingegangenen zu verarbeitenden Nachricht */
private int guid;
/**
* Aufruf des Konstruktors der Oberklasse und Initialisierung der
* zugehoerigen Instanz von PeerToPeerAnwendung
*
* @param server
* @param socket
* @param peerToPeerAnwendung
*/
PeerToPeerServerMitarbeiter(PeerToPeerServer server, Socket socket,
PeerToPeerAnwendung peerToPeerAnwendung) {
super(server, socket);
Main.debug.println("INVOKED-2 ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (PeerToPeerServerMitarbeiter), constr: PeerToPeerServerMitarbeiter("+server+","+socket+","+peerToPeerAnwendung+")");
this.peerToPeerAnwendung = peerToPeerAnwendung;
}
/** Methode zum versenden von Antwortnachrichten */
void senden(String nachricht) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (PeerToPeerServerMitarbeiter), senden("+nachricht+")");
if (socket != null && socket.istVerbunden()) {
try {
socket.senden(nachricht);
}
catch (Exception e) {
e.printStackTrace(Main.debug);
}
}
}
/**
* Zugriff auf die GUID der Anfrage-Nachricht, die durch diesen
* Mitarbeiter-Thread verarbeitet wird.
*
* @return
*/
int holeGuid() {
return guid;
}
/**
* Diese Operation verarbeitet eine eingegangene HTTP-Anfrage:
* <ol>
* <li> Lesen der gesuchten Datei aus lokalem Dateisystem </li>
* <li> Wenn die Datei vorhanden ist, Versenden der Datei; wenn die Datei
* nicht vorhanden ist, verschicken einer HTTP-Antwort mit dem
* Fehler-Status-Code 404 </li>
* <li> Versenden der Antwort ueber den geoeffneten Socket </li>
* </ol>
*
* @param element
* die zu verarbeitende HTTP Anfrage in Form eines
* TcpPufferElements
*/
private void httpAnfrageVerarbeiten(String nachricht) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (PeerToPeerServerMitarbeiter), httpAnfrageVerarbeiten("+nachricht+")");
HTTPNachricht http, antwort;
Datei datei;
http = new HTTPNachricht(nachricht);
antwort = new HTTPNachricht(HTTPNachricht.SERVER);
datei = peerToPeerAnwendung.holeDatei(http.getPfad());
if (datei != null) {
antwort.setStatusCode(200);
antwort.setContentType(datei.getDateiTyp());
antwort.setDaten(datei.getDateiInhalt());
}
else {
antwort.setStatusCode(404);
}
try {
socket.senden(antwort.toString());
}
catch (Exception e) {
e.printStackTrace(Main.debug);
}
}
/**
* Methode zur Verarbeitung einer eingehenden Ping-Nachricht.
* <ol>
* <li> Absender des Ping-Pakets wird der Liste der bekannten Teilnehmer im
* Peer-to-Peer-Netzwerk hinzugefuegt. </li>
* <li> Erzeugen einer entsprechenden Pong-Nachricht, wenn auf diese
* Ping-Nachricht nicht schon geantwortet wurde. </li>
* <li> Erhoehung des Hop-Zahlers und Dekrementierung des TTL-Zaehlers der
* Ping-Nachricht. </li>
* <li> Weiterleitung des Ping-Pakets </li>
* </ol>
*
* @param pingPaket
*/
private void verarbeitePing(PingPaket pingPaket) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (PeerToPeerServerMitarbeiter), verarbeitePing("+pingPaket+")");
String pongNachricht;
peerToPeerAnwendung.hinzuTeilnehmer(pingPaket.getIp());
pongNachricht = peerToPeerAnwendung.erstellePong(pingPaket).toString();
if (pongNachricht != null) {
try {
socket.senden(pongNachricht);
}
catch (Exception e) {
e.printStackTrace(Main.debug);
}
}
pingPaket.setHops(pingPaket.getHops() + 1);
pingPaket.setTtl(pingPaket.getTtl() - 1);
guid = pingPaket.getGuid();
peerToPeerAnwendung.sendePing(pingPaket, socket.holeZielIPAdresse());
}
/**
* Methode zur Verarbeitung einer eingehenden Suchanfrage (Query).
* <ol>
* <li> Erstellen der Ergebnisliste fuer eine Suchanfrage. Wenn auf die
* Query-Nachricht bereits geantwortet wurde, wird keine Liste erzeugt.
* Ausserdem wird damit zugleich veranlasst, dass die Suchanfrage
* ggf. weitergeleitet wird.
* </li>
* <li>
* <ul>
* <li> Wenn eine Liste mit mindestens einem Eintrag vorliegt, wird fuer
* jede Datei eine Antwortnachricht ueber den Socket verschickt (Query-Hit).
* </li>
* </ul>
* </li>
* </ol>
*
* @param queryPaket
*/
private void verarbeiteQuery(QueryPaket queryPaket) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (PeerToPeerServerMitarbeiter), verarbeiteQuery("+queryPaket+")");
LinkedList<Datei> dateien;
Datei aktuelleDatei;
Betriebssystem bs;
QueryHitPaket antwortPaket;
guid = queryPaket.getGuid();
dateien = peerToPeerAnwendung.verarbeiteAnfrage(socket
.holeZielIPAdresse(), queryPaket);
if (dateien != null && dateien.size() > 0) {
bs = (Betriebssystem) peerToPeerAnwendung.getSystemSoftware();
for (int i = 0; i < dateien.size(); i++) {
aktuelleDatei = (Datei) dateien.get(i);
antwortPaket = new QueryHitPaket(dateien.size(), 6346, bs
.holeIPAdresse(), "2", "", " ");
antwortPaket.setGuid(queryPaket.getGuid());
antwortPaket.setHops(0);
antwortPaket.setTtl(8);
antwortPaket.setErgebnis(aktuelleDatei.getName() + ": "
+ aktuelleDatei.holeGroesse() + " B");
try {
socket.senden(antwortPaket.toString());
}
catch (Exception e) {
e.printStackTrace(Main.debug);
}
}
}
}
/**
* Wenn eine Nachricht auf dem zu ueberwachenden Socket eintrifft, wird die
* Verarbeitung an diese Methode delegiert. <br />
* Unterschieden wird eine HTTP-GET-Anfrage, eine eingehende Ping-Nachricht
* und eine eingehende Suchanfrage (Query).
*/
protected void verarbeiteNachricht(String nachricht) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (PeerToPeerServerMitarbeiter), verarbeiteNachricht("+nachricht+")");
PeerToPeerPaket paket;
PingPaket pingPaket;
QueryPaket queryPaket;
if (nachricht != null) {
if (nachricht.startsWith("GET")) {
httpAnfrageVerarbeiten(nachricht);
}
else {
paket = new PeerToPeerPaket(nachricht);
guid = paket.getGuid();
if (paket.getPayload().equals("0x00")) {
pingPaket = new PingPaket(nachricht);
verarbeitePing(pingPaket);
}
else if (paket.getPayload().equals("0x80")) {
queryPaket = new QueryPaket(nachricht);
verarbeiteQuery(queryPaket);
}
}
}
}
}