Tutorial

SourceCode findet sich im Module phynixx-tutorial.

Grundlagen finden sich in Getting Started. Diese helfen Ihnen, die Schritte im Tutorial leicht nachzuvollziehen.

Das Projekt Phynixx bietet eine einfache Möglichkeit für Ressourcen an Transaktionen teilnehmen zu nehmen, seien es lokale oder globale (XA-protokoll, 2PC-) Transaktionen. Um eine Vorstellung vom Programmiermodell zu erhalten, stellen Sie sich einer herkömmliche Datenbankverbindung javax.sql.Connection vor. Diese Verbindung ist transaktional und kann, je nach zu grundeliegender DatenSource, auch an XA-Transaktionen teilnehmen.
Neben der Connection ist die DateSource in ihrer Rolle als ConnectionFactory wichtig.

Auch im Programmiermodell von Phynixx wird ihre transaktionale Ressource als Connection bezeichnet, ebenso existiert eine ConnectionFactory um die Connections zu erzeugen.

Fahrplan des Tutorials

In diesem Tutorial wird beschrieben, wie welche Voraussetzungen geschaffen werden, dass eine Ressource am Transaktionsprotokoll teilnehmen kann. Als Beispiel dient sequemtielles Schreiben in eine Datei. Dies soll transaktional unterstützt werden und solwohl in an lokalen als auch globalen Transaktionen teilnehmen können.

Beispiel

In eine Datei kann sequentiell geschrieben werden. Diese Schreiboperationen sollen transaktional unterstützt werden, so dass auch eine Rollback möglich ist. Dazu wird der Bereich des gültigen Inhalts durch eine Positionsangabe relativ zum Dateianfang gegeben. Mittels dieser Positionsangabe wird ein eventuelles Rollback implementiert, denn mittels der Dateiposition kann der Inhalt der Datei relativ zu dieser Position wiederhergestellt werden.

Aus der Implementierung ergibt sich, dass nur sequentiell in die Datei geschrieben werden kann.

Im Rahmen einer Transaktion soll bei einem Rollback der Stand der Datei zu Beginn der Transaktion wiederhergestellt werden. Die Anfangsdateiposition wird aus der aktuellen Länge der Datei ermittelt.

Folgender Testfall beschreibt die Arbeit mit dieser Klasse org.csc.phynixx.tutorial.TAEnabledUTFWriter

package org.csc.phynixx.tutorial;
 . . . 
public void testTAEnabledUTFWriter() throws Exception {

  File file = this.tmpDir.assertExitsFile("my_test.tmp");

  TAEnabledUTFWriter writer = TAEnabledUTFWriter.createWriter(file);
  try {
      // schreibe zwei String in die Datei
      writer.write("AA").write("BB");
  } finally {
     // schliesst Datei, laesst aber die Inhalt bestehen.
     writer.close();
   }

   // Liest den Inhalt der Datei wieder ein
   writer = TAEnabledUTFWriter.recoverWriter(file);
   try {
     List<String> content = writer.getContent();
     Assert.assertEquals(2, content.size());
     Assert.assertEquals("AA", content.get(0));
     Assert.assertEquals("BB", content.get(1));
   } finally {
       writer.close();
  }      
}

Listing 1 : Arbeit mit der Ressource TAEnabledUTFWriter

 

lokale Transaktionen

Diese Funktionalität des Schreibens in eine Datei soll transaktional unterstützt werden. Dazu muss sie im ersten Schritt das Interface org.csc.phynixx.connection.IPhynixxConnection unterstützen. Dort wird geregelt, wie die transaktionale Ressource (in unserem Fall TAEnabledUTFWriter) auf die unterschiedlichen Situationen innerhalb einer Transaktionn reagieren soll. Insbesondere wird eine commit- und rollback-Methode bereitgestellt.

Methode Beschreibung
rollback Die Datei soll auf den initialen Inhalt zurückgesetzt werden
commit Nichts geschieht, da Datei fortlaufend beschrieben wird und damit im Gutfall konsistent ist
reset Connection wird neu genutzt und der bisherige Zustand wird verworfen
close Connection wird nicht weiter genutzt und freigesetzt

Tabelle 1 :Implementierungen des Interfaces IPhynixxConnection für TAEnabledUTFWriter <nbsp;> Die Funktionalität zum seqnetiellen Schreiben finden sich in TAEnabledUTFWriter, eine Subklasse von IPhynixxConnection. Dort werden folgende Methoden implementiert

Methode Beschreibung
open Öffnet eine Datei zum schreiben. Es wird die aktuelle Position der Datei als ‘rollback’-Information gesichert
resetContent Inhalt der Datei wird verworfen.
readContent Liest den Inhalt aus Datei
write Es wird ein String in die Datei geschrieben.
Tabelle 2 :Funktionalität von TAEnabledUTFWriter  

Die Methoden open, resetContent, write verändern den Zustand der Ressource und müssen daher an einer Transaktion teilnehmen, damit diese korrekt funktionieren. Dies wird durch die Annotation @RequiresTransaction angezeigt.

Um sicherzustellen, dass die Datei auch bei unvorhergesehenem Abbruch der Transaktion wiederherzustellen ist, wird ein persistenter XADataRecorder angefordert. Mittels diesem können Wiederherstellungsinformation gesichert werden. Dazu muss das Interface import org.csc.phynixx.connection.IXADataRecorderAware implementiert werden. Sobald eine Transaktion geöffnet wird, so wird der eine XARecoder via setXADataRecorder(IXADataRecorder xaDataRecorder) injeziert.

Es ist sichergestellt, dass der IXADataRecorder injeziert wird, unmittelbar bevor die erste Methode aufgerufen wird, welche durch _@RequireTransaction_ annotiert ist. Die Injezierung wird durch den Aufruf dieser Methode ausgelöst.

Das Zusammenspiel innerhalb einer (lokalen) Transaktion ist in der Testklasse TransactionalBehaviourTest zu beobachten.

@Test
public void testCommit() throws Exception {

    File file = this.tmpDir.assertExitsFile("my_test.tmp");

    TAEnabledUTFWriter connection = this.connectionFactory.getConnection();
    connection.open(file);
    try {
        connection.write("AA").write("BB");
        connection.commit();
    } finally {
        connection.close();
    }

    TAEnabledUTFWriterImpl recoverWriter = new TAEnabledUTFWriterImpl();
    try {
        recoverWriter.open(file);
        List<String> content = recoverWriter.getContent();
        Assert.assertEquals(2, content.size());
        Assert.assertEquals("AA", content.get(0));
        Assert.assertEquals("BB", content.get(1));

    } finally {
        recoverWriter.close();
    }
}

Listing 2 : Beipiel die Einbindung der Ressource TAEnabledUTFWriter in eine lokale Transaktion

@Test
public void testRollback() throws Exception {

    File file = this.tmpDir.assertExitsFile("my_test.tmp");

    TAEnabledUTFWriter connection1 = this.connectionFactory.getConnection();
    try {
        connection1.open(file);
        connection1.write("AA").write("BB");            
        connection1.commit();
    } finally {
        connection1.close();
    }

    TAEnabledUTFWriter connection2 = this.connectionFactory.getConnection();
    try {
        connection2.open(file);
        connection2.write("CC").write("DD");
        connection2.rollback();
    } finally {
        connection2.close();
    }

    TAEnabledUTFWriter recoverWriter = this.connectionFactory.getConnection();
    recoverWriter.open(file);
    try {
        List<String> content = recoverWriter.getContent();
        Assert.assertEquals(2, content.size());
        Assert.assertEquals("AA", content.get(0));
        Assert.assertEquals("BB", content.get(1));

    } finally {
        recoverWriter.close();
    }
}

Listing 3 : Beipiel die Einbindung der Ressource TAEnabledUTFWriter in eine lokale Transaktion mit Rollback

managed Connection

Eine Connection in ihrer reinen Form gegeben durch die Implementierung TAEnabledUTFWriterImpl reicht nicht aus, um innerhalb einer 2 PhaseCommit-Transaktion zu agieren. Selbst um an lokalen Transaktionen in einfacherweise teilzunehmen, sollte die Connection eine managed connection sein. - Bereitstellung eines Persistenzmechanismus für rollback/rollforward-Daten - Teilnahme am Recoveryverfahren - Unterstützung von autocommit - Unterstüptzung von ConnectionPooling - Integration in spring transaktion management

Um alle diese Aspekte zu erhalten, muss eine PhynixxManagedConnectionFactory eingesetzt werden. Sie veredelt eine normale Connection zu einer managed connection und damit zu einer transaktionalen Ressource.

this.connectionFactory =
            new PhynixxManagedConnectionFactory<TAEnabledUTFWriter>(new TAEnabledUTFWriterFactoryImpl());

Listing 4 : Beispiel einer PhynixxManagedConnectionFactory

Connection, welche mit dieser Factory erzeugt worden sind, sind voll funktiontionsfähige transaktionale Ressourcen und können an lokalen Transaktionen teilnehmen. Jede managed connection besitzt eine assoziierte connection; bei uns vom Typ TAEnabledUTFWriter. Bei Bedarf wendet sich die managed connection an diese.

Folgendes Listing zeigt die Einbindung und Konfiguration einer managed connection factory

private PhynixxManagedConnectionFactory<TAEnabledUTFWriter> connectionFactory = null;

@Before
public void setUp() throws Exception {
    // configuring the log-system (e.g. log4j)
    TestUtils.configureLogging();
    this.tmpDir = new TmpDirectory("test");

    this.connectionFactory =
            new PhynixxManagedConnectionFactory<TAEnabledUTFWriter>(
                            new TAEnabledUTFWriterFactoryImpl());
    IDataLoggerFactory loggerFactory = new FileChannelDataLoggerFactory(
                             "ta_enabled", this.tmpDir.getDirectory());
    IPhynixxLoggerSystemStrategy<TAEnabledUTFWriter> strategy = 
            new LoggerPerTransactionStrategy<TAEnabledUTFWriter>(loggerFactory);
    connectionFactory.setLoggerSystemStrategy(strategy);
}

Listing 5 : Setup der Testumgebung

  • Die ConnectionFactory TAEnabledUTFWriterFactoryImpl liefert die Connections der Ressource TAEnabledUTFWriter. Sie wird zusammen mit TAEnabledUTFWriterFactory bereitgestellt.
  • Um einen persistenten XADataRecorder injezieren zu können, muss eine eine Persistenzverfahren an die Factory übergeben werden, welches die Wiederherstellungsinformationen sichert. In dieser Strategie wird festgelegt, auf welche Weise die Wiederherstellungsinformationen gesichert werden. Die Strategie LoggerPerTransactionStrategy erzeugt DataRecoder im Filesystem.
  • Um einfach temporäre Dateien erzeugen zu können, bietet die Klasse TmpDirectory ein Interface, temporäre Datei und Verzeichnisse zu erzeugen

Workflow einer managed connection

Der Workflow einer managed connection innerhalb einer Transaktion kann mittels eines Listeners (siehe Observer Pattern [GoF] ) beobachtet werden. Auf diese Weise kann auch die Funktionalität einer managed connection erweitert werden.

Dazu muss ein Listener vom Typ IPhynixxManagedConnectionListener implementiert werden. Um die ManagedConnectionFactory zu beauftragen, jede managed connection mit diesem Listener zu verzieren, wird ihr ein IPhynixxManagedConnectionDecorator übergeben werden. Dieser wird bei der Instanzierung einer managed connection gerufen und kann diese wie gewünscht verändern.

Mittels dies Prinzips kann eine ManagedConnection auch um eigene Aspekte erweitert werden.

  connectionFactory.addConnectionProxyDecorator(
             new DumpManagedConnectionListener<TAEnabledUTFWriter>())

Listing 6 : Beobachten des Workflows einer managed connection

DumpManagedConnectionListener implementiert sowohl IPhynixxManagedConnectionListener als auch IPhynixxManagedConnectionDecorator

Wiederherstellungsinformationen

Wiederherstellungsinformationen werden über IXADataRecorder gesichert. Sobald ihre Ressource das Interface IXADataRecorderAware wird eine solcher Recorder injeziert.

Wiederherstellungsinformationen sind notwendig, wenn eine Transaktion aus welchen Gründen auch immer nicht korrekt beendet wurde.

rollback-Informationen werden genutzt, um die Ressource auf den Stand bei Beginn der Transaktion wiederherzustellen.

rollforward-Informationen werden genutzt, um eine begonnenes commit konsistent zu beenden.

stellung muss ein rollback ausgeführt werden, um die Ressource auf den Stand bei beginn der Transaktion zu ährend Informationen, welche genutzt werden, um bei rollback die Konsistenz der Ressource wiederherzustellen. - Informationen, welche genutzt werden, um bei

Konfiguration von AutoCommit

Soll ihrer transaktionale Ressource für lokale Transaktionen autocommit unterstützen, so muss sie das Interface IAutoCommitAware unterstützen.

Globale Transaktionen

Integration mit Spring

TBD

lokale Transaktionen

globale Transaktionen