/* Listing 1a: <I>FinalizeDemo<I> in Java */

import java.util.*;

class DemoBaseClass {
    public DemoBaseClass() {
        System.out.println("DemoBaseClass.DemoBaseClass()");
    }    
    public void finalize() {
        System.out.println("DemoBaseClass.finalize()");
    }    
}

/**
 * Beispielprogramm zur Demonstration
 * des Aufrufzeitpunkts der Methode finalize().
 * @author Michael Tamm
 */
public class FinalizeDemo extends DemoBaseClass {

    public FinalizeDemo() {
        System.out.println("FinalizeDemo.FinalizeDemo()");
    }

    public void finalize() {
        System.out.println("FinalizeDemo.finalize()");
        super.finalize();
    }

    public static void f() {
        Object x = new FinalizeDemo();
    }

    public static void main(String[] args) {
        f();
        // ganz lange Berechnung ...
        System.out.println("Starte ganz lange Berechnung");
        Vector v = new Vector();
        Random r = new Random();
        for (int i = 0; i < 10000; ++i) {
            v.add(new Integer(r.nextInt()));
        }
        int sum = 0;
        for (Iterator i = v.iterator(); i.hasNext(); ) {
            Number n = (Number)i.next();
            sum += n.intValue();
        }
        System.out.println("Fertig mit ganz langer Berechnung");
    }
}


/* Listing.1b: <I>FinalizeDemo<I> in C# */

using System;
using System.Collections;

class DemoBaseClass {
    public DemoBaseClass() {
        Console.WriteLine("DemoBaseClass.DemoBaseClass()");
    }
    ~DemoBaseClass() {
        Console.Out.WriteLine("DemoBaseClass.~DemoBaseClass()");
    }
}

/// <summary>
/// Beispielprogramm zur Demonstration
/// des Aufrufzeitpunkts der Methode Finalize().
/// Autor: Michael Tamm
/// </summary>
class FinalizeDemo : DemoBaseClass
{
    public FinalizeDemo() {
        Console.Out.WriteLine("FinalizeDemo.FinalizeDemo()");
    }

    ~FinalizeDemo() {
        Console.Out.WriteLine("FinalizeDemo.~FinalizeDemo()");
    }

    public static void f() {
        Object x = new FinalizeDemo();
    }

    [STAThread]
    static void Main(string[] args) {
        f();
        // ganz lange Berechnung ...
        Console.Out.WriteLine("Starte ganz lange Berechnung");
        ArrayList v = new ArrayList(40000);
        Random r = new Random();
        for (int i = 0; i < 10000; ++i) {
            v.Add(r.Next());
        }
        int sum = 0;
        for (IEnumerator i = v.GetEnumerator(); i.MoveNext(); ) {
            int n = (int)i.Current;
            sum += n;
        }
        Console.Out.WriteLine("Fertig mit ganz langer Berechnung");
    }
}

/* Listing 2: Fehlerhafte <I>Logger<I>-Klasse */

import java.io.*;
import java.text.*;
import java.util.*;

/**
 * Fehlerhafte Klasse zum Loggen von Nachrichten
 * mit Zeitstempel - siehe Artikel.
 */
public class Logger
{
    static final DateFormat TIMESTAMP_FORMAT =
        new SimpleDateFormat("[HH:mm:ss.SSS]");

    private Writer _logWriter;

    public Logger(File f) throws IOException {
        _logWriter = new BufferedWriter(new FileWriter(f));
    }
    
    public synchronized void log(String msg) throws IOException {
        StringBuffer buf = new StringBuffer();
        buf.append(TIMESTAMP_FORMAT.format(new Date()));
        buf.append(' ');
        buf.append(msg);
        buf.append('\n');
        _logWriter.write(buf.toString());
    }

    public static void main(String[] args) {
        System.out.println("Teste Logger ...");
        try {
            Logger myLogger = new Logger(new File("test.log"));
            myLogger.log("Das ist ein Test.");
        }
        catch(IOException x) {
            System.err.println("Error:" + x);
        }
        System.out.println("Done.");
    }
}


*/ Listing 3: Schreiben in eine Datei */

FileStream fs = new FileStream("test.txt", 
    FileMode.Open, FileAccess.Write, FileShare.Read);

StreamWriter sw = new StreamWriter(fs);

sw.Write("Hallo");

// Zum Schluss muss unbedingt die Close-Methode
// des StreamWriter-Objekts aufgerufen werden
sw.Close();

// Das FileStream-Objekt muss nicht explizit
// geschlossen werden, da StreamWriter.Close()
// die Datei automatisch schließt.


/* Listing 4: Korrigierte <I>Logger<I>-Klasse */


import java.io.*;
import java.text.*;
import java.util.*;

/**
 * Klasse zum Protokollieren von Meldungen
 * mit einem Zeitstempel.
 * <p>
 * Achtung, rufen Sie die Methode close() als letzte
 * Methode für Logger-Objekte auf, um sicherzustellen,
 * dass alle Lognachrichten in die Logdatei geschrieben
 * werden.
 * <p>
 * Beispielcode:<pre>
 *     Logger myLogger = null;
 *     try {
 *         myLogger = new Logger(new File("..."));
 *         ...
 *         myLogger.log("...");
 *         ...
 *     }
 *     finally {
 *         if (myLogger != null) myLogger.close();
 *     }
 * </pre>
 */
public class Logger
{
    static final DateFormat TIMESTAMP_FORMAT = 
        new SimpleDateFormat("[HH:mm:ss.SSS]");

    private Writer _logWriter;
    private boolean _isClosed;
    private String _constructLocation;

    /**
     * Konstruiert einen Logger, der in die angegebene
     * Datei <code>f</code> schreibt.
     */
    public Logger(File f) throws IOException {
        _logWriter = new BufferedWriter(new FileWriter(f));
        _isClosed = false;
        _constructLocation = getConstructLocation(new Throwable());
    }

    /**
     * Versucht aus dem Stacktrace des Throwable-Objekts herauszufinden,
     * wo das Logger-Objekt konstruiert wurde.
     */
    private String getConstructLocation(Throwable t) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        t.printStackTrace(pw);
        String st = sw.toString();
        int i = st.indexOf("Logger.<init>");
        if (i == -1) return "";
        i = st.indexOf("at ", i);
        if (i == -1) return "";
        i += 3; // ... skip "at "
        return st.substring(i, st.indexOf(")", i) + 1);
    }
    
    /**
     * Schreibt <code>msg</code> mit einem Zeitstempel
     * versehen in die Logdatei.
     * <p>
     * Diese Methode darf nicht mehr aufgerufen werden,
     * nachdem das Logger-Objekt mit der Methode close()
     * geschlossen wurde.
     * 
     * @see close
     * 
     * @throws IOException wenn ein Fehler beim Schreiben in
     *          die Logdatei aufgetreten ist.
     * @throws IllegalStateException wenn diese Methode nach
     *          einem Schliessen des Logger-Objekts via close()
     *          aufgerufen wird.
     */
    public synchronized void log(String msg) throws IOException {
        if (_isClosed) {
            throw new IllegalStateException("Logger bereits geschlossen.");
        }
        StringBuffer buf = new StringBuffer();
        buf.append(TIMESTAMP_FORMAT.format(new Date()));
        buf.append(' ');
        buf.append(msg);
        buf.append('\n');
        _logWriter.write(buf.toString());
    }

    /**
     * Schließt die Logdatei.
     * <p>
     * Diese Methode muss als letzte Methode von Logger-Objekten
     * aufgerufen werden, um sicherzustellen, dass alle Lognachrichten
     * in die Logdatei geschrieben werden.
     * 
     * @throws IOException wenn ein Fehler beim Schliessen der
     *          Logdatei aufgetreten ist.
     */
    public void close() throws IOException {
        if (!_isClosed) {
            _logWriter.close();
            _isClosed = true;
        }
    }

    /**
     * Wird automatisch vom Garbage Collector aufgerufen.
     */
    protected void finalize() throws Throwable {
        if (!_isClosed) {
            System.err.println(
                "FEHLER: Die Methode close() wurde für das an der Stelle " +
                _constructLocation + 
                " konstruierte Logger-Objekt nicht aufgerufen."
            );
            try {
                this.close();
            }
            catch (Throwable x) {
                // Hier können wir nichts mehr machen.
            }
        }
        super.finalize();
    }

    /**
     * Einfacher Test, der die Nachricht "Das ist ein Test."
     * in die Datei test.log im aktuellen Verzeichnis schreibt.
     */
    public static void main(String[] args) {
        System.out.println("Teste Logger ...");
        try {
            Logger myLogger = null;
            try {
                myLogger = new Logger(new File("test.log"));
                myLogger.log("Das ist ein Test.");
            }
            finally {
                if (myLogger != null) myLogger.close();
            }
        }
        catch(IOException x) {
            System.err.println("Error:" + x);
        }
        System.out.println("Done.");
        System.runFinalizersOnExit(true);
    }
}

