package javacodebook.io.filelock;

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

/**
 * Sperren einer Datei während der Schreib-/Leseoperationen. Dadurch kann
 * ein Datenaustausch mit anderen Programmen durchgeführt werden. Zum testen
 * kann dieses Programm zweimal aufgerufen werden, so dass 2 JVMs gestartet
 * werden. Dadurch wird ein konkurrierender Zugriff erzeugt.
 */
public class LockFileTest {

    //Die Methode versucht, die Datei zu sperren und eine neue Sequenznummer
    //zu erzeugen
    public static int newSequenceNum(FileChannel channel) {
        int seqNum = -1;
        ByteBuffer buffer = ByteBuffer.allocate(4);
        try {
            //Erstmal versuchen, die Datei exklusiv zu öffnen
            FileLock lock = channel.tryLock();
            //wenn erfolgreich, dann Inhalt der Datei lesen, Zahl um 1 erhöhen
            //und wieder in die Datei schreiben
            if(lock != null) {
                channel.position(0);
                channel.read(buffer);
                buffer.rewind();
                seqNum = buffer.getInt() + 1;
                buffer.clear();
                //Erhöhten Zahlenwert in den Buffer schreiben und sofort zurückspulen
                buffer.putInt(seqNum).rewind();
                channel.position(0);
                channel.write(buffer);
                //Sperrung der Datei aufheben
                lock.release();
            }
        } catch(IOException e) {
            e.printStackTrace(System.out);
        }
        return seqNum;
    }

    public static void main(String[] args) throws Exception {
        if(args.length < 1)
            printUsage();
        File syncFile = new File(args[0]);

        //Datei zum beliebigen Lesen und schreiben öffnen
        RandomAccessFile file = new RandomAccessFile(syncFile, "rw");
        //Einen Channel öffnen
        FileChannel channel = file.getChannel();
        //Mit der Zufallszahl soll der Prozess von einem parallel laufenden
        //anderen Java-Prozess unterschieden werden
        int myId = new java.util.Random().nextInt(100);
        //Ausgabe in Log-Datei schreiben zwecks Nachverfolgung
        File logFile = new File(syncFile.getParent() + File.separator
            + "channel" + myId + ".log");
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(logFile)));

        //Zugriffe auf die Sequenzdatei starten
        for(int i = 0; i < 1000; i++) {
            int seqNum = newSequenceNum(channel);
            if(seqNum == -1)
                out.println("Sequenz blockiert");
            else
                out.println("Aktuelle Sequenznummer für Kanal " + myId + ": " + seqNum);
            Thread.sleep(10);
        }

        //Channel und Dateien schliessen
        channel.close();
        file.close();
        out.close();
    }

    private static void printUsage() {
        System.out.println("Aufruf: java javacodebook.io.filelock.LockFileTest Dateiname");
        System.exit(0);
    }
}
