/**
 * Democode, der die Programmierung eines Servlets verdeutlicht.
 *
 * Aufgabe: Das Servlet zeigt dem Benutzer eine Eingabeseite
 *          an. Hier kann der Name eines Professors eingegeben
 *          und das Ausgabeformat des Ergebnisses ausgewählt werden:
 *          - XML in HTML: zeigt den XML-Code als Teil einer
 *                         HTML-Seite an.
 *          - XML: Generiert ein reines XML-Dokument
 *          - Tabelle: Hier wird unter Verwendung von XSL und
 *                     XSLT aus dem XML-Ergebnis eine HTML-Tabelle
 *                     erzeugt.
 *
 * Hinweis: Das Servlet wurde am Lehrstuhl für Dialogorienterte
 *          Systeme an der Universität Passau entwickelt und
 *          funktioniert in dieser Form nur am Pool des Lehrstuhls.
 *          Soll das Servlet in anderen Umgebungen ausgeführt werden,
 *          müssen verschiedene Anpassungen vorgenommen werden (z.B.
 *          die Datenbank-Anbindung.)
 *
 * @author Stefan Seltzsam 
 */

package demoservlet;

// Klassen für die Servlet-Programmierung
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletContext;
import javax.servlet.ServletConfig;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Klassen für XSL und XSLT
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;


// Normales Java-Zeug
import java.io.*;
import java.sql.*;
import java.text.*;

public class DemoServlet extends HttpServlet
{
  /**
   * HTML-Code für die Eingabeseite.
   */
  private String page_request = "";
  
  /**
   * HTML-Code für die Seite, in die die Antwort eingebettet wird.
   */
  private String page_answer = "";

  /**
   * Die Datenbankverbindung.
   */
  private DatabaseConnection dbconn = null;

  /**
   * Typkonstanten
   */
  private static int XML = 0;
  private static int XML_IN_HTML = 1;
  private static int HTML = 2;

  // -- Servlet-Methoden ------------------------------------------------------
  
  /**
   * Die Init-Methode des Servlets.
   */
  public void init(ServletConfig config) throws ServletException
  {
    super.init(config);

    // lade die vordefinierten Seiten ein...
    page_request = readPageFromDisk("request.shtml");
    page_answer = readPageFromDisk("answer.shtml");

    // initialisiere die Datenbank
    try
    {
      dbconn = new DatabaseConnection();
    }
    catch(SQLException sqle)
    {
      log("Could not initialize database: ", sqle); 
      throw new UnavailableException("Could not initialize database: " + sqle);
    }
  }

  /**
   * Handler fuer die HTTP GET Methode.
   */
  public void doGet (HttpServletRequest request,
                     HttpServletResponse response)
    throws ServletException, IOException
  {
    doPost(request, response);
  }

  /**
   * Handler fuer die HTTP POST Methode
   */
  public void doPost (HttpServletRequest request,
                      HttpServletResponse response)
    throws ServletException, IOException
  {
    // was soll angezeigt werden?
    String action = request.getParameter("action");

    if (action == null || action.equals("request"))
      printRequestPage(response);
    else if (action.equals("answer"))
      printAnswerPage(request, response);
    else 
      printRequestPage(response);
  }

  /**
   * Erledigt Aufräumarbeiten.
   */
  public void destroy()
  {
    dbconn.close();
  }

  // -- Methoden zur Seitengenerierung ----------------------------------------

  /**
   * Gibt die Seite zur Abfrage des Namens des Professors aus.
   *
   * @param     response     Das HttpServletResponse-Objekt, das dem Servlet 
   *                         uebergeben wurde.
   */
  private void printRequestPage(HttpServletResponse response)
    throws IOException
  {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println(page_request);
    out.close();
  }

  /**
   * Gibt die Seite mit den Abfrageergebnissen aus.
   *
   * @param     request      The request object to use.
   * @param     response     The response object to use.
   */
  private void printAnswerPage(HttpServletRequest request,
                               HttpServletResponse response)
    throws IOException
  {
    // welcher Ergebnistyp wurde vom Benutzer eingestellt?
    String typ = request.getParameter("typ");

    if (typ.equals("XML in HTML"))
      printAnswerPage(request, response, DemoServlet.XML_IN_HTML);
    else if (typ.equals("XML"))
      printAnswerPage(request, response, DemoServlet.XML);
    else
      printAnswerPage(request, response, DemoServlet.HTML);
  }

  /**
   * Gibt die Seite mit den Abfrageergebnissen als XML
   * Seite (entweder in HTML kodiert oder direkt).
   *
   * @param     request      The request object to use.
   * @param     response     The response object to use.
   * @param     type         XML, XML_IN_HTML or HTML (Konstanten!)
   */
  private void printAnswerPage(HttpServletRequest request,
                               HttpServletResponse response,
                               int type)
    throws IOException
  {
    // Ergebnistyp setzen
    if (type == XML_IN_HTML || type == HTML)
      response.setContentType("text/html");
    else
      response.setContentType("text/xml");

    // Anfrage generieren und Ergebnis von der Datenbank holen
    PrintWriter out = response.getWriter();
    String query = generateQuery(request);
    String db_answer = dbconn.getResultAsXML(query);

    // Inhalt der Seite erzeugen
    if(type == XML)
      // Datenbankergebnis = XML-Seite
      out.println(db_answer);
    else if (type == XML_IN_HTML)
    {
      // An der Stelle der Antwortseite, die mit @@@answer gekennzeichnet ist,
      // wird das XML-Ergebnis eingebettet. Zeichen wie < und > muessen dabei
      // geeignet in HTML konvertiert werden
      String answer = replaceParameter(page_answer, "@@@answer", 
                                       "<pre>" + quote(db_answer) + "</pre>");
      out.println(answer);
    }
    else if (type == HTML)
    {
      try
      {
        // Verwende das Xalan-Toolkit und eine *.xsl Datei, um XML zu HTML zu
        // konvertieren
        StringWriter result = new StringWriter();
        convertXMLToHTML(new StringReader(db_answer),result);

        // Ergebnis der Konvertierung wird wieder statt @@@answer in das
        // Ergebnisdokument eingefuegt.
        String answer = replaceParameter(page_answer, "@@@answer", result.toString());
        out.println(answer);
      }
      catch (Exception e)
      {
        // Es ist ein Fehler aufgetreten...
        log("Fehler beim Konvertieren von XML zu HTML: " + e);
      }
    }
    
    out.close();
  }

  // -- Hilfsmethoden ---------------------------------------------------------

  /**
   * Laedt eine HTML-Seite von Platte.
   */
  private String readPageFromDisk(String filename)
    throws ServletException
  {
    StringBuffer ret = new StringBuffer();
    try
    {
      filename = getServletContext().getRealPath(filename);
      BufferedReader reader = new BufferedReader(new FileReader(filename));
      String line = null;
      do 
      {
        line = reader.readLine();
        if (line != null)
          ret.append(line + "\n");
      } while (line != null);
    }
    catch (Exception e)
    {
      log("Could not load \"" + filename + "\": ", e); 
      throw new UnavailableException("Could not load \"" + filename + "\": " + e);
    }
    
    return ret.toString();
  }
  
  /**
   * Generiert die Anfrage aus dem request-Objekt.
   * Diese SQL-Anfrage gibt alle Vorlesungen aus, die ein
   * vom Benutzer gegebener Professor liest.
   */
  private String generateQuery(HttpServletRequest request)
  {
    return "select v.vorlnr, v.titel, v.sws "
      + "from professoren p ,vorlesungen v where p.name = '" +
      request.getParameter("professor_name") + "' and v.gelesenvon = p.persnr";
  }

  /**
   * Diese Methode ersetzt in einem gegebenen String einen
   * substring durch einen anderen String.
   *
   * @param     orig    Der Original-Text.
   * @param     flag    Der Substring, der ersetzt werden soll.
   * @param     rep     Der Text, der eingefuegt werden soll.
   * @return    Der neue Text.
   */
  private String replaceParameter(String orig, String flag, String rep)
  {
    int from = orig.indexOf(flag);
    
    if (from == -1)
      return orig;
    
    StringBuffer buff = new StringBuffer(orig);
    buff.replace(from, from + flag.length(), rep);

    return buff.toString();
  }

  /** 
   * Liest XML-Daten aus dem gegebenen <code>java.io.Reader</code>,
   * konvertiert die Daten ins HTML-Format und schreibt das Ergebnis
   * in den gegebenen <code>java.io.Writer</code>. Die Konvertierung
   * basiert dabei auf einem XSL-Dokument.
   *
   * @param	in	Das Reader-Objekt, aus dem die XML-Daten gelesen werden.
   * @param	out	Das Writer-Objekt, in das das Ergebnis geschrieben wird.
   *
   * @exception	Exception	if the conversion fails.
   */
  protected void convertXMLToHTML(Reader in, Writer out) throws Exception
  {
    // Instanziiere eine TransformerFactory
    TransformerFactory tFactory = TransformerFactory.newInstance();  
    
    // Generiere mit Hilfe der TransformerFactory und dem Stylesheet einen Transformer
    Transformer transformer = 
	tFactory.newTransformer(new StreamSource(getServletContext().getRealPath("queryresult.xsl")));
    
    // Fuehre die Konvertierung mit Hilfe des Transformers durch.
    transformer.transform(new StreamSource(in), new StreamResult(out)); 
  }
  
  /**
   * Diese Methode ersetzt Zeichen eines Strings, die so nicht in
   * einem HTML-Dokument vorkommen duerfen, durch entsprechende
   * Codes, die in HTML dann korrekt ausgegeben werden.
   *
   * @param     org     Der Ursprungs-String.
   * @return    Modifizierter String.
   */
  private String quote(String org)
  {
    StringBuffer ret = new StringBuffer();
    StringCharacterIterator itr = new StringCharacterIterator(org);

    for(char c = itr.first(); c != CharacterIterator.DONE; c = itr.next()) 
    {
      switch(c)
      {
      case '<':
        ret.append("&lt;");
        break;
      case '>':
        ret.append("&gt;");
        break;
      case '&':
        ret.append("&amp;");
        break;
      case '"':
        ret.append("&quot;");
        break;
      default:
        ret.append(c);
      }
    }

    return ret.toString();
  }
}                               
