package demoservlet;

import java.sql.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.servlet.ServletContext;

/**
 * Diese Klasse implementiert eine sehr einfache Datenbankschnittstelle.
 * Die Ergebnisse der Datenbank werden als String, der ein XML-Dokuemnt
 * enthaelt, zurueckgegeben.
 */
public class DatabaseConnection 
{
  /**
   * String, der die DB-Verbindung beschreibt. Dieser muss an die
   * lokalen Verhaeltnisse, in der diese DatabaseConnection verwendet wird,
   * angepasst werden.
   */
 
  private static final String driver = "JDBC-Treiber";
  private static final String conURL = "JDBC-Connection-String";
  private static final String user = "User zum Verbinden zur DB";
  private static final String pwd  = "Passwort";


  /**
   * Verbindung zur Datenbank
   */
  private Connection conn = null;

  /**
   * Hashtable um SQL-Typnummern auf Strings abzubilden
   */
  private HashMap<Integer,String> sqltypenames = new HashMap<Integer,String>();

  /**
   * Der Header einer XML-Seite.
   */
  private String XML_Header = "<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>\n";

  /**
   * Constructor
   *
   * @exception SQLException    Wenn es ein Problem mit der Datenbankverbindung gibt.
   */
  public DatabaseConnection()
    throws SQLException
  {
    // Initialisierung der Datenbankverbindung
    try {
      Class.forName(driver);      
      conn = DriverManager.getConnection(conURL, user, pwd);  
      initializeSqltypenames();
    }
    catch (Exception e) {
      if (e instanceof SQLException)
        throw (SQLException)e;
      else
        throw new SQLException("Folgender Fehler ist bei der Initialisierung aufgetreten: " + e);
    }
  }

  /**
   * Diese Methode nimmt eine Anfrage entgegen, gibt sie an
   * die Datenbank weiter und erzeugt aus den Ergebnissen der
   * Datenbank ein XML-Dokument.
   *
   * @param     query   Die SQL-Anfrage an die Datenbank.
   *
   * @return    Das Ergebnis als XML-Dokument (Top-Level-Element <result>)
   *            oder einen Fehler (Top-Level-Element <error>).
   */
  public String getResultAsXML(String query)
  {
    Statement sql_stmt = null;
    StringBuffer ret = new StringBuffer();
    ret.append(XML_Header + "<result>\n");
    
    // Generiere das XML-Ergebnis
    int i = 0;
    try 
    {
      sql_stmt = conn.createStatement();
      ResultSet rset = sql_stmt.executeQuery(query);
      
      // Schema des Ergebnisses ermitteln
      ResultSetMetaData meta = rset.getMetaData();
      int attributes = meta.getColumnCount();
      String[] attrib_names = new String[attributes+1];

      for (i = 1; i <= attributes; i++)
        attrib_names[i] = meta.getColumnName(i).toLowerCase();

      // Schema-Informationen als XML....
      ret.append("  <metadata>\n");

      for (i = 1; i <= attributes; i++)
      {
        ret.append("    <attribute>\n");
        ret.append("      <name>" + attrib_names[i] + "</name>\n");
        ret.append("      <type>" + typeIntToString(meta.getColumnType(i)) + "</type>\n");
        ret.append("    </attribute>\n");
      }

      ret.append("  </metadata>\n");

      // Ergebnis Tupel fuer Tupel ausgeben
      while(rset.next())
      {
        ret.append("  <tuple>\n");
        for (i = 1; i <= attributes; i++)
          ret.append("    <" + attrib_names[i] + ">" + rset.getString(i) + "</" + attrib_names[i] + ">\n");
        ret.append("  </tuple>\n");
      }
      
      // Aufraeumen
      rset.close();
      sql_stmt.close();
    }
    catch(SQLException se) 
    {
      // Falls ein Fehler aufgetreten ist...
      return (XML_Header + "<error>\n" + se + "</error>\n");
    }

    ret.append("</result>");
    return ret.toString();
  }

  /**
   * Gibt einen beschreibenden String fuer eine
   * SQL-Typkonstante aus.
   */
  private String typeIntToString(int type)
  {
    String ret = null;

    ret = (String)sqltypenames.get(new Integer(type));

    if (ret == null)
      ret = "unknown";

    return ret;
  }

  /**
   * Initialisiert die Hashmap, die verwendet wird,
   * um SQL-Typnummern auf Strings abzubilden.
   */
  private void initializeSqltypenames()
  {
    // Die Namen der Typen stehen als Namen von Konstanten 
    // in der Klasse java.sql.Types.... also verwenden wir
    // diese, um sqltypenames zu initialisieren.
    try
    {
      Field[] types = Class.forName("java.sql.Types").getFields();
      
      for (int i = 0; i < types.length; i++)
        sqltypenames.put(new Integer(types[i].getInt(null)), types[i].getName().toLowerCase());
    }
    catch (Exception e)
    {
      // sollte nicht auftreten...
    }
  }

  /**
   * Schliesst die Verbindung.
   */
  public void close()
  {
    try {
      conn.close();
    } 
    catch (SQLException e) {
      // ... irgendwas ist schiefgegangen ...
    }
  }
}
