[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ]

Sobald die Komponente B die Transaktion in Schritt 7 durchführt, passiert nicht alles. Komponente B erbt den Transaktionsstatus von Komponente A. Dadurch kann sie die Transaktion nicht vollständig durchführen. Die wirkliche Transaktion wird in Schritt 8 beendet, sobald das letzte Objekt mit einem Transaktionsstatus verschwindet. Zu diesem Zeitpunkt werden die Änderungen zurückgeführt, die in den Tabellen 'customer' und 'orders' durchgeführt wurden, da diese in einem SQL SERVER gespeichert sind. Die Änderungen in der VFP Datenbank, die Email-Benachrichtigung und die Einträge in der Textdatei bleiben jedoch erhalten. Bei der Ausführung von SetAbort()/SetCommit() werden nur Resource Manager (Datenbanken) beeinflußt, deren Transaktionen durch den MS DTC gesteuert werden.

Die Transaktionsunterstützung wird für jede Komponente separat eingestellt. Allerdings können Transaktionen auch über mehrere Pakete verteilt sein. Die Einstellung für die Transaktionsunterstützung einer Komponente wird bei den Eigenschaften einer Komponente eingestellt.

Es gibt 4 verschiedene Einstellungen

 

Einstellung Beschreibung

Requires a transaction

falls das aufrufende Objekt in einer Transaktion arbeitet, arbeitet die Komponente in der gleichen Transaktion. Falls das aufrufende Objekt keine Transaktion unterstützt, erzeugt das Objekt eine neue Transaktion

Requires a new transaction

es wird immer eine neue Transaktion erzeugt

Supports transactions

Falls die aufrufende Komponente eine Transaktion unterstützt, arbeitet die Komponente in der gleichen Transaction

Does not support transactions

Die Komponente unterstützt keine Transaktionen

SetComplete bzw. SetAbort erfüllen zwei Zwecke. Einerseits führen sie Durchführung (Commit) einer Transaktion oder deren Abbruch (Rollback) herbei, andererseits deactivieren sie Objekte, um sie in den zustandslosen Modus zu überführen. Tatsächlich können diese beiden Methoden verwenden werden, um die JIT Activation zu unterstützen, ohne jegliche Gedanken an eine Transaktionsunterstützung. SetComplete ermöglicht die Freigabe von Ressourcen/Speicher, um die Skalierbarkeit der Anwendung zu erhöhen. Das Kontextobjekt stellt einige andere Methoden zur Verfügung, die für Transaktionen hilfreich sind: EnableCommit, DisableCommit und IsInTransaction. Das nachfolgende Beispiel zeigt, wie man Transaktionen in VFP verwalten kann :

LPARAMETER tcCustID

LOCAL llFound, loMTX, loContext

 

loMTX = CREATEOBJECT("MTXAS.APPSERVER.1")

loContext = loMTX.GetObjectContext()

 

USE customer AGAIN SHARED

LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)

llFound = FOUND()

IF FOUND()

loContext.SetComplete()

ELSE

loContext.SetAbort()

ENDIF

 

RETURN llFound

In diesem Szenario kann man sich vorstellen, daß eine andere Komponente schon eine Aktualisierung auf die Tabelle 'orders' vorgenommen hat. Falls die entsprechende 'CustomerId' nicht gefunden wird, wird die Transaktion zurückgeführt.

In diesem Szenario wird mit Remote-Ansichten auf eine SQL SERVER-Tabelle gearbeitet. Transaktionen mit VFP Tabellen werden nicht unterstützt.

Tip: Die Verbindung zu den Remote Daten müssen ohne jeglichen Login-Dialog geschehen. Die Eigenschaft DispLogin muß auf 3 – DB_PROMPTNEVER stehen. Bei dem Zugriff über SPT, kann man die SQLSETPROP()-Funktion dazu verwenden :

SQLSETPROP(0, 'DispLogin', DB_PROMPTNEVER)

Verteilte Transaktionen:

Transaktionen können normalerweise nur auf einen einzigen Resource Manager zugreifen. Ein Resource Manager ist einfach eine Server Resource, die in die Transaktion eingebunden ist. Normalerweise werden Resource Manager als Datenbanken betrachtet. Obwohl der am meisten anzutreffende Fall ist, ist dies nicht die einzige Art von Resource Managern.

Vorteilhaft wäre es, wenn Transaktionen sich über mehrere Datenbanken erstrecken könnten. Dies soll am Beispiel einer Bank erklärt werden. Ein Bank ist in mehrere Sparten gegliedert und jede Sparte verwaltet lokal die Daten ihrer Konten, die zu ihrer Sparte gehören.

Man öffnet zwei Konten in verschiedenen Sparten und möchte nun einen Geldbetrag von einem Konto in Sparte zu einem Konto in Sparte B transferieren. Um dies durchzuführen, muß der Geldbetrag in dem Konto in Sparte A abgezogen werden und dem Konto in Sparte B hinzugezählt werden. Ohne verteilte Transaktionen kann der Geldbetrag zwar erfolgreich in Sparte A abgezogen werden, aber der Betrag konnte dem Konto in Sparte B aus irgendwelchen Gründen nicht gutgeschrieben werden. Bei einzelnen Transaktionen in Sparte A und Sparte B ist das Geld solange verloren , bis die Transaktion in Sparte B schließlich durchgeführt werden konnte.

Mit einer verteilten Transaktion, die beide Datenbanken umfaßt, würde der Abbruch in Sparte B auch die Änderungen in Sparte A zurückführen. Dadurch wäre kein Geld kurzfristig verloren gegangen.

Verteilte Transaktionen verwenden das sogenannte Two-Phase -Commit Protokoll und einen Transaktions-koordinator. Die verschiedenen Phasen werden vom dem Distributed Transaction Coordinator (DTC) verwaltet. Der DTC benachrichtigt alle Resource Manager, die in der Transaktion enthalten sind, um die Durchführung vorzubereiten. In diesem Schritt muß der Resource Manager sicher sein, daß die Durchführung stattfindet und gibt an den DTC einen Erfolg oder einen Mißerfolg weiter. Sobald alle Resource Manager sich gemeldet haben, teilt ihnen der DTC mit, ob sie die Transaktion durchführen sollen oder zuückfahren. Sobald alle ok zurückgemeldet haben, teilt der DTC ihnen mit, daß sie die Änderungen durchführen sollen. Falls ein beteiligter Resource Manager ein negatives Ergebnis zurückgibt, teilt der DTC allen beteiligten Resource Manager mit, daß die Änderungen zurückgestzt werden sollen

Erzeugen eines MTS Servers

Nun wird ein MTS COM-Server erzeugt. Dies ist recht einfach, wenn man schon einmal eine VFP COM-Komponente erzeugt hat.

einer VFPErzeugen COM Komponente

  1. Erzeugen einer neuen Projektdatei genannt PRJSERVER.PJX
  2. Erzeugen einer neuen Programmdatei (PRG) genannt TEST1.PRG
  3. Fügen Sie den follgenden Code zu diesem Programm hinzu:

DEFINE CLASS clsServer AS CUSTOM OLEPUBLIC

 

   PROCEDURE Hello

      RETURN "Hallo Welt!"

   ENDPROC

 

ENDDEFINE
  1. Unter ERSTELLEN wählt man COM-DLL ERSTELLEN ... und erstellt die Dateii PRJSERVER.DLL. Alle MTS Komponenten müssen als InProc-DLL Server erstellt werden. Der Server kann nun in VFP getestet werden.

loServer=CREATEOBJECT("prjServer.clsServer")

? loServer.Hello()

Hinzufügen einer VFP COM Komponente zu einem MTS Paket

Ein Paket ist eine Auflistung von Komponenten, die in demselben Prozess laufen. Pakete definieren Grenzen für einen Serverprozess, der auf einem Servercomputer läuft. Wenn eine Sales-Komponente und eine Purchasing-Komponente in unterschiedliche Pakete exportiert werden, laufen diese in unterschiedlichen Prozessen. Sollte die Sales-Komponente unerwartet abbrechen, hat dies keine Auswirkung auf die Purchasing-Komponente.

Nachfolgend wird beschrieben, wie eine VFP COM-Komponente in die MTS-Umgebung installiert wird.

  1. Starten Sie den MTS Explorer
  2. Im linken Fenster suchen Sie den Eintrag ‘My Computer’ in der Computers-Auflistung
  3. Klicken Sie auf den ‘Packages Installed’ Knoten, um alle standardmäßig installierten Pakete zu sehen. Pakete sind eine Ansammlung von Komponenten, die zusammenhängende Funktionen innerhalb der Anwendung haben.
  4. Erzeugen Sie ein neues Paket mit ACTION – NEW – PACKAGE
  5. Betätigen Sie die CREATE NEW PACKAGE Schaltfläche. Geben Sie einen Namen für das Paket an (z.B. FoxTest).
  6. Danach betätigen Sie NEXT und FINISH. Danach ist das Paket in der Auflistung der Pakete enthalten.
  7. Klicken Sie auf den Knoten des neu erstellten Pakets (FoxTest). Jetzt sehen Sie 2 Unterpunkte. In dem ‘Components’-Ordner können Sie neue Komponenten hinzufügen, wie Sie eben in Visual FoxPro erstellt haben. In dem ‘Roles’-Ordner können Sie die Zugriffsrechtsverwaltung für das Paket definieren.
  8. Klicken Sie ACTION – NEW – COMPONENT,
  9. Betätigen Sie die Schaltfläche INSTALL NEW COMPONENT, um den Installationsdialog für neue Komponenten darzustellen. Klicken Sie auf ADD FILES und suchen den Speicherort Ihrer DLL (PRJSERVER.DLL). Wählen Sie sowohl die DLL als auch die TLB aus. Danach sehen Sie die Komponente als schwarze Kugel mit einem grünen Kreuz in der Auflistung der Komponenten.
  10. Damit ist das Paket vollständig und einsatzbereit.

Zugriff auf die Komponente

Der Zugriff auf die Komponente erfolgt mit dem gleichen Code,wie man auch die COM-Komponente aufruft

loServer = CREATEOBJECT("prjServer.clsServer")

? loServer.Hello()

Wenn man den MTS Explorer wieder öffnet, sieht man wie die Komponente rotiert. Mit der Option ‘Status View’ kann man den aktuellen Zustand der Komponente feststellen.

Wenn man das Objekt (RELEASE loServer)aus dem Speicher löscht, entfernt MTS dessen Referenz.

Nun stellt sich die Frage, was eigentlich bei einem Zugriff auf eine MTS-Komponente geschieht ?

Der einfachste Weg um zu sehen, was intern passiert, ist, den Client Request vom Anfang bis zum Ende nachzuverfolgen. Wir haben eine Anwendung, die auf einem Client C läuft, die einen Aufruf an die MTS Komponente M auf dem Server S stellt. Die Komponte M greift auf eine Datenbank auf einem anderen Server D zu.

C benutzt DCOM um eine Instanz der Komponente M anzulegen. Da M ein In-Proc-Server ist, kann dieser via DCOM nicht benutzt werden. Hier beginnt die Aufgabe des Transaction Server. Er ändert einige Einträge in der Registry. Wenn eine Komponente in einem Package registriert wird, editiert der Transaction Server die CLSID der Komponente. Der InprocServer32-Schlüssel wird entfernt uns fügt einen LocalServer32-Schlüssel hinzu, der auf den Transaction Server zeigt. Weiterhin wird die CLSID Package gespeichert, die die Komponente enthält. Dann sieht es ungefähr folgendermaßen aus :

LocalServer32 = MTX.EXE /p:{968690F2-1E2B-11D0-BEC5-00AA00A2FA25}

MTX.EXE arbeitet als eine Host-Prozeß für die Komponente. Tatsächlich arbeitet er als Host-Prozeß für alle Komponenten in dem Package.

Wenn die DCOM-Anfrage den Transaction Server erreicht, sieht es die MTX.EXE als lokalen Server und instanziert ihn. Dies bedeutet, daß die DCOM-Anfrage die Schnittstelle der MTX.EXE benutzt. Diese überprüft die CLSID und IID und benutzt ihrerseits COM, um mit der Komponente zu kommunizieren und ein Objekt daraus zu instanzieren. Danach sendet MTX.EXE einen Wrapper an den Client, der wie die Komponente aussieht, so daß der Client mit der MTX.EXE kommuniziert, diese aber wie die benötigte Komponente anspricht. In dem Wrapper befinden sich noch zusätzliche Informationen, die den sogenannten 'Kontext' darstellen. Da alle Aufrufe des Clients an die MTX.EXE gehen, hat der Transaction Server  Kenntnis über alle Vorgänge.

Weiterhin wird bei einem Start des MTX-Prozesses der ODBC Resource Dispenser geladen und das 'Connection Pooling' angeschaltet. Der DTC wird ebenfalls initialisiert. Wenn die Komponente instanziert ist, beginnt in Ihrem Auftrag eine DTC Transaktion.

Jetzt haben wir einen Client, der in dem Glauben ist, er hat einen Zeiger auf ein Objekt, welches in Wirklichkeit ein Wrapper ist. Wir haben einen Host-Prozeß (Server-Prozeß), der auf dem Server läuft und eine Komponente umschließt. Schließlich haben wir eine ODBC-Umgebung mit 'Connection pooling' aktiviert und eine mit der Komponente verbundene DTC Transaktion gestartet.

Nun ruft die Anwendung auf C eine Methode in M auf. Der Transaction Server überwacht den Methodenaufruf. Der Code der Methode wird ausgeführt und erzeugt eine ODBC-Umgebungs(-kennung). Dann erzeugt es eine ODBC-Verbindung. Wenn dies geschieht, macht der ODBC Resource Dispenser 2 Dinge. Er aktiviert das connection pooling und fragt MTS nach der Transaktion Id der Komponente. Transaction Server gibt diese Informatio zurück und der ODBC Resource Dispenser setzt eine Option, so daß die Connection in die DTC-Transaktion einbezogen wird.

Auf dem Resource Manager D wird der Befehl abgehandelt und die Transaktion beginnt. Der Resource Manager kommuniziert mit dem DTC, daß er die Transaktion begonnen hat und registriert diese im DTC.

Die Komponente führt Ihre Transaktionen durch, die in D ausgeführt werden. Wenn diese beendet sind, schließt es die Verbindung und die Umgebung. Der ODBC Resource Dispenser überprüft den Status der Verbindung und hält diese in seinem Pool oder schließt diese.

Der Code der Komponente benutzt deren Kontext, ruft 'SetComplete' auf, um klarzustellen, daß alles erledigt ist und kehrt von der Methode zurück. Jetzt führt MTS die DTC-Transaktion durch und löscht die Komponente, obwohl der Client noch eine Referenz darauf hat. Der Client hat jedoch nur eine Referenz auf den Wrapper. Beim nächsten Aufruf der Methode wird intern eine neue Instanz der Komponente erzeugt.

Sobald die DTC Transaktion durchgeführt ist, informiert der DTC D die Transaktion vorzubereiten und durchzuführen. Falls die Durchführung der Transaktion scheitert, D informiert den DTC, welcher MTS informiert, MTS informiert den Client. Solange der Methodencode ausgeführt wird, kann die Rückgabe der Methode geändert werden, falls das Fehlschlagen der Transaktion vorgesehen ist. Dies sollte beim Design der Komponente vorgesehen werden.

Schließlich wird der Client die Referenz auf die Komponente entfernen. Nun kann man erwarten, daß der MTS-Prozeß ebenfalls beendet wird, da er von keiner Komponente mehr benutzt wird. Standardmäßig bleibt der Prozeß noch 3 Minuten erhalten, so daß andere Anfragen nicht erst den MTS-Prozeß wieder starten müssen. In COM kann sich ein Server selbständig registrieren, wenn er startet, so daß andere Anfragen für ein bestimmtes Objekt diesen Typs zu der bestehenden Instanz umgeleitet werden und dadurch verhindert wird, daß eine neue Instanz erstellt wird. MTX.EXE führt dies für die Objekte durch, für die sie als Host dient, so daß alle Anfragen zu dem gleichen Prozeß gelangen.

Weiterführende Gedanken

Bis jetzt wurden die Grundlagen zur Installation eines VFP Servers in MTS vorgestellt. Es wurde eine VFP Komponente in einen MTS Prozess eingehüllt, der die Zugriffsrechte, Transktionszustand und andere allgemeine Aufgaben verwaltet. Alle VFP Server, die mit MTS benutzt werden, werden in dieser Art registriert. Um die Vorteile der MTS-Laufzeitumgebung zu benutzen, kann die Komponente direkt mit dieser kommunizieren. Weiterhin kann die Registrierung komplett automatisiert werden, da MTS eine Administrationsschnittstelle als COM-Interface zur Verfügung stellt.

Programmiermodell

Kontextobjekt

Das Kontextobjekt-Modell ist das Modell, das man für die Erzeugung von MTS-Anwendungen verwenden sollte. Die Komponente muß speziell für MTS entwickelt werden, kann dann aber von den Vorteilen von MTS Gebrauch machen.

Auf das Kontextobjekt kann folgendermaßen zugegriffen werden:

#DEFINE MTX_CLASS "Mtxas.AppServer.1"

 

LOCAL loMTX, loContext

loMtx = CREATEOBJECT(MTX_CLASS)

loContext = loMtx.GetObjectContext()

Shared Property Manager

Der Shared Property Manager (SPM) ist ein MTS Resource Dispenser. Der SPM erlaubt die Erzeugung von globalen Eigenschaften und deren gemeinsamen Zugriff von unterschiedlichen Komponenten desselben Paketes. Alle Komponenten des gleichen Paketes können damit auf gemeinsame Eigenschaften zugreifen, jedoch können SharedProperties nicht zwischen unterschiedlichen Paketen ausgetauscht werden.

Der SPM stellt auch eine Möglichkeit für ein Objekt dar, seinen Zustand festzuhalten, bevor es deaktiviert(zustandslos) wird. JIT-Activation hat keine Auswirkung auf den Status des SPM.

Das nachfolgende Beispiel zeigt, wie man den SPM mit Visual FoxPro Servern benutzen kann :

#DEFINE MTX_CLASS            "MTxAS.AppServer.1"

#DEFINE MTX_SHAREDPROPGRPMGR "MTxSpm.SharedPropertyGroupManager.1"

PROCEDURE GetCount (tlReset)

   LOCAL loCount

   LOCAL loMTX,loContext

   LOCAL lnIsolationMode,lnReleaseMode,llExists

 

   loMTX = CREATEOBJECT(MTX_CLASS)

   loContext = loMTX.GetObjectContext()

   loSGM = loContext.CreateInstance(MTX_SHAREDPROPGRPMGR)

 

   lnIsolationMode = 0

   lnReleaseMode = 1

 

   * Gibt eine Referenz auf die Gruppe zurück,

   * in der die Eigenschaft vorhanden ist

   loSG = loSGM.CreatePropertyGroup("CounterGroup", lnIsolationMode,;

                                     lnReleaseMode, @llExists)

  

   * Gibt eine Referenz auf die Eigenschaft zurück

   loCount = loSG.CreateProperty("nCount", @llExists)

 

   * überprüft, ob die Eigenschaft schon existiert,

   * anderenfalls wird sie zurüchgesetzt

   IF tlReset OR !llExists

      loCount.Value = 1

   ELSE

      loCount.Value = loCount.Value + 1

   ENDIF

 

   RETURN loCount.Value

ENDPROC

Die folgenden Einstellungen sind für den Isolation und Release Modus möglich:

Isolation Mode Beschreibung

LockSetGet 0

(default) Sperrt die Eigenschaft während eines Aufrufs des Wertes, um sicherzustellen, daß jedes Lesen(ACCESS) und Schreiben(ASSIGN) eindeutig ist. Dies stellt sicher, daß nicht 2 Clients gleichzeitig auf die gleiche Eigenschaft lesen oder schreiben können, aber es verhindert nicht, daß andere Clients auf andere Eigenschaften der gleichen Gruppe zugreifen können.

LockMethod 1

Sperrt alle Eigenschaften in der Shared Property Group für den exklusiven Zugriff für die aufrufende Komponente, solange die aktuelle Methode der aufrufenden Komponente ausgeführt wird. Dies ist sinnvoll, wenn bestimmte Abhängigkeiten zwischen den einzelnen Eigenschaften einer Gruppe existieren, oder in Fällen, wo ein Client sofort nach dem Lesen einer Eigenschaft diese Aktualisieren muß, bevor auf diese von einem anderen Client zugegriffen werden kann.

 

 

Release Mode:

 

Standard  0

(default) Sobald alle Komponenten Ihre Referenz zu der SPG gelöst haben, wird die SPG automatisch gelöscht.

Process  1

Die SPG wird erst dann zerstört, wenn der MTS-Prozess endet. Man muß die SPG-Objekte separat zerstören, indem man Sie auf NULL setzt.

Erzeugen der unterschiedlichen Layer

Data Layer

Hier wird der Zugriff auf die Datenbanken programmiert. Im Normalfall wird eine Komponente für eine Tabelle entwickelt, die standardmäßig die folgenden Eigenschaften hat :

Name Aufgabe

Insert()

Hinzufügen eines neuen Datensatzes

Delete()

Löschen eines Datensatzes

Update()

Ändern eines Datensatzes

GetData()

Rückgabe der Referenz eines ADO-Recordsets

Nachfolgend Beispiele, wie eine 'Update' bzw. 'GetData'-Methode aussehen kann.

DEFINE CLASS accounts AS CUSTOM OLEPUBLIC

   oMTX = NULL

   oContext = NULL

 

PROCEDURE Accounts.GetData

LPARAMETERS tnAccountNr

LOCAL loMTX, loContext

 

* Parameter überprüfen

IF VARTYPE(tnAccountNr) == "N" THEN

   lcSQL = "SELECT * FROM accounts WHERE account_nr = " + ;

            ALLTRIM(STR(tnAccountNr))

ELSE

   lcSQL = "SELECT * FROM accounts"

ENDIF

 

* ObjektContext zuweisen

THIS.oMTX = CREATEOBJECT(MTX_CLASS)

THIS.oContext = THIS.oMTX.GetObjectContext()

IF ISNULL(THIS.oContext) THEN

   RETURN NULL

ENDIF

 

* Verbindung zur Datenbank herstellen

loAdoConn = CREATEOBJECT("ADODB.Connection")

loAdoConn.Open("dsWest", "sa", "")

 

* Recordset öffnen

loAdoRs = CREATEOBJECT("ADODB.Recordset")

loAdoRs.Open(lcSQL, loAdoConn)

 

IF loAdoRs.Eof And loAdoRs.Bof THEN

   RETURN NULL

ELSE

   RETURN loAdoRs

ENDIF

ENDPROC

 

PROCEDURE Accounts.Update

 

LPARAMETERS tnAccountNr, tyAmount

 

LOCAL lnHnd

 

LOCAL loMTX, loContext

 

THIS.oMTX = CREATEOBJECT(MTX_CLASS)

THIS.oContext = THIS.oMTX.GetObjectContext()

 

lnHnd = SQLCONNECT("dsWest", "sa", "")

IF lnHnd < 0 THEN

   RETURN ERROR_NO_CONNECTION

ENDIF

 

lnExec = SQLEXEC(lnHnd, "EXEC sp_update(?tnAccountNr, ?tyAmount)")

IF lnExec != ERROR_SUCCESS THEN

   loConText.SetAbort()

   RETURN ERROR_EXECUTE

ENDIF

 

SQLDISCONNECT(lnHnd)

 

RETURN ERROR_SUCCESS

 

ENDPROC

 

PROCEDURE Error

   THIS.oContext.SetAbort()

ENDPROC

 

ENDDEFINE

[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ]