Dienstag, 30. August 2011

Nachtrag zur Erweiterung des Domänenmodells der HTI

Der Kern der Regel

Was der eigentliche Kern einer Regel ist, wurde bisher noch nicht ausreichend betrachtet. Zuletzt wurde eine Regel als ein Objekt mit einem Namen, einem Regelinhalt (content) und Abhängigkeiten dargestellt. Bei weiteren Gedanken zur Umsetzung ist aufgefallen, dass eine Regel nicht nur als ein Regel-Dokumentteil mit einem Inhalt betrachtet werden darf. Der Inhalt besteht aus einem Bedingungsteil (LHS) und einem Konsequenzteil (RHS). Dabei ist es der LHS-Teil lediglich Drools-XML-Code. Der RHS-Teil ist indes ein Abschnitt mit Java-Code in dem diverse Aktivitäten gekapselt werden, die als Konsequenzhandlung ausgeführt werden sollen. Das zukünftige Ziel ist die Konfiguration von Regeln bzw. von Konsequenzen der Regeln in einem Editor oder Wizard. Denkbar ist es, dass per Drag-And-Drop einzelne Aktionen in den Konsequenzteil hineingezogen werden, sortiert, und mit den entsprechenden Aktionsparametern eingefügt, eine komplette Regel-Konsequenz erstellt wird. Dies ist nur möglich durch eine starke Kapselung von Funktionalitäten. Dem widerspräche die bisherige Modellierung des Regel-Inhalts. Im Modell sollte diese feingranulare Struktur abgebildet werden. Demnach besteht der Konsequenzteil also aus atomaren Aktionen, die entweder in Modulen bereits zur Verfügung stehen, oder speziellen Java-Anweisungen, die für eine Regel spezifisch sein können. Die Klassenstruktur der nochmals angepassten Regel müsste demnach wie folgt aussehen:
Nun besitzt Rule eine Liste vom Typ Consequence, um den RHS-Teil abzubilden. Für den LHS-Teil dagegen, genügt es aktuell als String gespeichert zu werden.

Montag, 29. August 2011

Erweiterung des Domänenmodells der HTI

Die Ursache für die Erweiterung des Domänenmodells der HTI ist das Hinzukommen einer Monitoring-Komponente. Diese sollte dazu dienen, dass sog. Service-Level-Agreements (SLA) eingehalten werden können. Sie hat also eine Überwachungs- und Steuerungsfunktion. Als geeignete Technologie für die zu realisierenden Funktionen wurde die Rule-Engine Drools ausgewählt. Um dieses regelbasierte System in die HTI zu integrieren bedarf es zunächst einer Erweiterung des Modells um Datenstrukturen für SLAs und Regeln. Zwar wurde sich bereits in einer Bachelorarbeit mit der notwendigen Erweiterung befasst. Diese Arbeit richtete den Fokus jedoch mehr auf die allgemeine Machbarkeit und hatte einen eher theoretischen Charakter. Nun da es wieder in die Praxisphase geht, treten weitere Probleme und Gedanken zu dem Thema auf. Darum soll es im Folgenden gehen.


Regeln und umgebende Entitäten
Drools-Regeln sind nichts anderes als Teile von XML-Regel-Dokumenten. Diese bestehen aus einem Dokumentelement und einer Dokumententität. Der eigentliche Inhalt eines XML-Dokuments ist in Form der Kinderelemente dem Dokumentelement angehangen.
Die eigentliche Regel ist also ein Kind des Dokumentelementes <package/>.

Vom XML-Dokument zur Java-Klasse
Für die Erweiterung des bisherigen Domänenmodells ist nun von Bedeutung, in wie weit man die Teile der XML-Regeln in Java-Klassen abbilden möchte. Da die Beschreibung des Domänenmodells, also der Java-Klassen für die HTI in XML-Schemas erfolgt, ist es ein leichtes den gesamten Sprachumfang von Drools-XML-Regeln zu unterstützen. Es ist jedoch fraglich, ob die aus dem XML-Schema generierten Java-Klassen tatsächlich benötigt werden. Unnötige Komplexität soll vermieden werden.
Darum muss die Frage gestellt werden, welche Sprachmerkmale der XML-Regeldokumente werden gebraucht und wie werden diese am sinnvollsten im Domänenmodell abgebildet. Es gibt folgende Ansätze der Abbildung:

1
Einbinden des 
Drools-Schemas in das Modellschema
·     Der komplette Sprachumfang wird unterstützt.
·     Es wird jedoch nicht alles gebraucht
·     Das Modell wird unnötig komplex.
2
Erweiterung des 
Modellschemas um die notwendigen Sprachelemente der XML-Regelsprache
·     Das Modell bleibt überschaubar.
·     Zusätzliche Erweiterungen bedeuten eine Anpassung des Systems.
·     Es muss genau herausgestellt werden auf welche Sprachelemente verzichtet werden kann, und auf welche nicht.
·     Werden Regeln in separaten Objekten gehalten, spielt beim Laden der Regeln die Reihenfolge eine besonders bedeutende Rolle. Dies ist bei der Wahl des Container-datentyps (List/Map/Set) für Regeln und  bei der Implementierung des Ladevorgangs zu berücksichtigen.
3
Speicherung des XML-Regel-Dokuments als String
·     Die Veränderungen am Modell sind dafür minimal.
·     unnötige Redundanz

Resultat der Auswertung: Ziel der Konfiguration des Monitorings mit Hilfe von Regeln war es, für jede Task-Instanz eine spezielle Regel angeben zu können. Dies wird dadurch erreicht, dass eine Regel mit Patterns genau beschreibt, welche Task-Instanz eine Regel erfüllen soll. Da alle Regeln bei der Initiierung des Monitoring-Prozesses komplett in den Production-Memory der Rule-Engine geladen werden müssen, sind Redundanzen unerwünscht. Das heißt es wird der Ansatz Nr. 2 verfolgt.

Beziehung der Java-Regelklasse(n) zum Service-Level-Agreement
Ein Service-Level-Agreement muss eingehalten in allen Fällen eingehalten werden. Der Kontrollmechanismus wird durch Regeln umgesetzt. Es ist möglich, dass ein SLA eine Beziehung zu mehreren Regeln besitzt, wobei es auch andere SLAs geben kann, die eine Beziehung zu denselben Regeln besitzen. Es besteht also eine Viele-Zu-Viele-Beziehung. Aufgelöst wird diese Beziehung durch sog. Wrapper-Klassen, die auf Grund der Modellierung per XML notwendig sind, um eine saubere XML-Datenstruktur zu beschreiben. Es gibt also eine Klasse ServiceLevelAgreements, die eine Liste von Objekten der Klasse ServiceLevelAgreement enthält. Analog zu dieser Modellierung mit Wrapper-Klassen, wird dies auf die Klassen Rules und Rule zu. Ein Objekt der Klasse ServiceLevelAgreement hält nun eine Referenz auf ein Rules-Objekt. Weiterhin werden die Beziehungen der Klassen durch folgendes gekennzeichnet. Da SLAs Kundenspezifisch sind, muss für einen neunen Kunden auch eine neues SLA angelegt werden. Auf Grund dieser Spezifik, sind SLAs für andere Kunden nicht zu gebrauchen. Daher besteht zwischen ServiceLevelAgreements und ServiceLevelAgreement eine Kompositionsbeziehung. Da es weiterhin mehrere SLAs geben kann, die einen Regelsatz (Rules) unabhängig voneinander verwenden können, besteht zwischen ServiceLevelAgreement und Rules lediglich eine Aggregationsbeziehung. Weil Regeln zu verschiedenen Regelsätzen orchestriert werden können, muss auch zwischen der Wrapperklasse Rules und der Klasse Rule eine Aggregationsbeziehung bestehen.

Persistenz und Kaskadierung
Es wurde bereits festgestellt, dass Redundanz von Regeln vermieden werden soll, wobei dies zum einen durch das Datenmodell für die Regeln gewährleistet wird. Zum anderen jedoch, ist durch die im vorherigen Abschnitt beschriebene Beziehung sichergestellt, dass mehrere SLAs dieselbe Regel verwenden können. Bei der Realisierung der Persistenz-Mechanismen muss indes dafür Sorge getragen werden, dass unerwünschte Seiteneffekte vermieden werden. Ein kurzes Beispiel erklärt das Problem:
Es gibt zwei SLAs. Beide haben eine Referenz auf dieselben Regeln gespeichert. Verändert ein SLA, als Beziehungsbesitzer der Regeln, beliebige Attribute von diesen oder löscht gar eine Regel, wird das andere von den beiden SLAs von diesen Änderungen auch betroffen sein, ohne, dass dies gewollt war.
Mögliche Manipulationen an den Regeln betreffen daher immer alle SLAs, die gemeinsame Regelreferenzen besitzen. Daher muss man bei der Regelmanipulation Vorsicht walten lassen. Konkrete Vorsichtsmaßnahmen sollten sein:
  1. Das Weiterreichen (Kaskadierung) von Persistenz-Operationen von SLA zu Regel muss eingeschränkt werden. Ähnlich wie bei der Beziehung zwischen Task-Instanz und Task-Beschreibung, soll zwar eine Referenz auf das Objekt existieren. Etwaige Manipulationen beim Speichervorgang sollen jedoch nicht weitergerecht werden. 
  2. Da die Kaskadierung eingeschränkt wird, muss es eine eigene Persistenz-Klasse geben, die alle Operationen separat ermöglicht. Es wird also ein Rule-DAO (Data Access Object) benötigt. Diese muss bei der Realisierung von Manipulationsmechanismen unterscheiden, ob diese für ein referenzierendes SLA oder für alle gelten soll.
Damit wird bei der Umsetzung der Persistenz-Schicht bereits ersichtlich, dass es Beschränkungen für die Manipulation von Regeln bzw. Service-Level-Agreements gibt.

Zusammensetzen und Validieren vom Regeldokumenten
Soll aus einem Java-Regelobjekt eine Regel in die Rule-Engine eingefügt werden, so muss die Form der Regel der XML-Regelform entsprechen. Das heißt, das Dokument muss aus den entsprechenden Teilen, die als Java-Objekte gespeichert sind, zu einem validen XML-Regeldokument zusammengesetzt werden. Es ist sinnvoll, dieses Zusammensetzen zu kapseln. Die Kapselung soll durch die Klasse RuleDocumentBuilder geschehen. Diese bekommt die „Einzelteile“ der Regeln als Klassenfelder gesetzt. Daraus soll das vollständige XML-Dokument generiert werden.
Ein valides XML-Regel-Dokument allein ist jedoch noch kein Erfolgsgarant für die Ausführung der Regeln. Können bspw. Java-Abhängigkeiten nicht aufgelöst werden, wird der Package-Builder von Drools die Ausführung verhindern. Daher wird neben dem Zusammensetzen eines validen Dokumentes auch noch eine Funktion benötigt, die das Dokument bzw. den enthaltenen Java-Code validiert. Dazu kann man sich bequemerweise ebenfalls des Package-Builders bedienen. 

Resultierendes Domänenmodell
Das vorläufig erweiterte Domänenmodell ist in dem folgend abgebildeten Klassendiagramm zu sehen. Die dargestellten Klassen beschränken sich nur auf die wesentlichen, unmittelbar von Änderungen betroffenen Klassen. Blau sind alle neuen Klassen dargestellt, rot die bereits bestehenden Klassen.
Erweitertes Domänenmodell

Neben den bisher beschriebenen Erweiterungen bleibt zu erwähnen, dass das ehemals in der Task-Beschreibung befindliche Attribut deadlines nun in der Klasse ServiceLevelAgreement zu finden ist.

Freitag, 26. August 2011

Ein Beispiel-Szenario für Drools-Regeln

In dem Post "Drools für meine Rules" habe ich auf ein später folgendes Hello-World-Beispiel verwiesen. Nun ist es soweit.

Eine Task-Instanz (TI) ist im Kontext dieses Systems das technische Äquivalent zu dem abstrakteren Begriff des Human Tasks. Die TI gehört also zum Domänen-Modell des Human Task Service (HTS). Für dieses Beipiel ist ein vereinfachtes Modell die Grundlage.

Jede TI wird zum Erzeugungszeitpunkt mit einem Zeitstempel versehen. Weiterhin ergibt sich aus der zugewiesenen Task-Beschreibung die maximale Dauer für die Bearbeitung einer Task-Instanz bzw. den Human Task. Diese muss für in jedem Fall zugesichert werden können, damit der HTS als wirtschaftliche Dienstleistung angeboten werden kann. Neben der Erfüllungsdauer gibt es noch weitere Gütemerkmale von Diensten. Diese werden in sog. Service Level Agreements definiert. Es muss für die Verwendung der Rule-Engine Drools also eine Regel formuliert werden, die den erklärten Sachverhalt erfüllt.

Hier kommt die Regel dazu:
<?xml version="1.0" encoding="UTF-8"?>
<package
    name="de.saxsys.hti.monitoring.controller"
    xmlns="http://drools.org/drools-5.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
    xs:schemaLocation="http://drools.org/drools-5.0 drools-4.0.xsd">
    <import name="de.saxsys.hti.monitoring.domain.*"/>
    <import name="de.saxsys.hti.monitoring.util.mail.*"/>
    <import name="de.saxsys.hti.monitoring.util.deadline.*"/>
    <import name="de.saxsys.hti.monitoring.handler.*"/>
    <import name="javax.xml.datatype.*"/>
    <import name="java.math.BigDecimal"/>
    <import name="java.util.*"/>

    <function
        return-type="String"
        name="prepareMessageText">
        <parameter
            identifier="$ti"
            type="TaskInstance"/>
        <body>             
            StringBuffer sb = new StringBuffer();
           
            sb.append("An task instance with state '").append($ti.getState().value());
            sb.append("' has passed the half of the deadline!\n");
            sb.append("--------------------------------------------\n");
            sb.append("Detailed data:\n\n");
            sb.append("id: ").append($ti.getId()).append("\n"); 
            sb.append("name: ").append($ti.getName()).append("\n");
            sb.append("creation: ").append($ti.getCreationTime()).append("\n");
           
            Date creationDate =    $ti.getCreationTime();
            String duration = $ti.getTaskDescription().getServiceLevelAgreement().getDeadline().getTimeSpan();
            sb.append("deadline: ").append(DeadlineChecker.getEndDate(creationDate, duration));
           
            return sb.toString();
        </body>
    </function> 

    <rule name="deadline is half passed">
        <lhs>
            <pattern object-type="TaskInstance" identifier="$ti">
                <and-constraint-connective>
                    <field-constraint field-name="state">
                        <qualified-identifier-restriction evaluator="!=">State.COMPLETED</qualified-identifier-restriction>
                    </field-constraint>
                    <field-constraint field-name="state">
                        <qualified-identifier-restriction evaluator="!=">State.FAILED</qualified-identifier-restriction>
                    </field-constraint>
                </and-constraint-connective>
            </pattern>
            <eval>
                DeadlineChecker.halfOfDeadlineHasPassed($ti.getCreationTime(), $ti.getTaskDescription().getServiceLevelAgreement().getDeadline().getTimeSpan())
            </eval>
        </lhs>
        <rhs>
            $ti.setPriority(Prioritizer.increase($ti.getPriority()));
            System.out.println("... increase priority to: " + $ti.getPriority().value());
            System.out.println("... inform responsible person of half passed deadline");
            String message = prepareMessageText($ti);
            HTIReactionHandler htiRH = (HTIReactionHandler) ReactionHandlerFactory.getReactionHandler(ReactionHandlerType.HTI_HANDLER);
            htiRH.sendMail("department_chief@anycompany.com", "Warning", message, $ti.getAttachment());
        </rhs>
    </rule>
</package>
Nun die Erläuterung des Dokuments:

  1. Zuerst das Wurzelelement: <package></package> Mit dem Attribut name wird eine Bezeichnung angegeben, welche die in diesem Dokument enthaltenen Regeln im Speicher der Rule-Engine eindeutig von anderen Regeln unterscheidet.
  2. In den Regeln kann Java-Codeenthalten sein. Die genutzten Klassen werden durch den  import-Tag geladen.
  3. Regeln werden durch das Element <rule/> ausgedrückt. Ein Regel besteht aus einem Bedingungsteil (left hand side) und dem Konsequenzteil (right hand side). Die Tags <lhs/> und <rhs/> besitzen diese Semantik und werden als Kindelemente des rule-Tags eingefügt.
Der Bedingungsteil:
  1. Eine Bedingung wird in Drools durch sog. Patterns, also Muster, ausgedrückt. Man beschreibt mit Patterns wie entsprechende Datenobjekte ausehen sollen, welche die Bedingung erfüllen sollen. Mit dem Attribut object-type wird der Typ bzw. die Klasse des Objekts festgelegt. Über einen Bezeichner, der mit dem Attribut identifier angegeben wird, kann man in dem Dokument auf das aktuelle Objekt zugreifen.
  2. Bisher würde die Bedingung lauten: "Objekte müssen vom Typ TaskInstance sein, damit der Konsequenzteil (rhs) aktiviert wird.". Darum werden mit Feld-Beschränkungen (Restriktionen) genauere Angaben zu dem Objekt gemacht. Mit dem field-constraint-Tag wird beschrieben, welche Felder das Objekt haben muss.
  3. Als Kindelemente des field-constraint-Tags können zum Beispiel folgende Elemente verwendet werden, um die Werte der Felder zu beschränken:
    • Um Enumerationen anzugeben, wird eine qualified-identifier-restriction verwendet.
    • Für alle anderen (primitiven bzw. standard) Typen kann der folgende XML-Tag benutzt werden: <literal-restriction evaluator=">" value="0"/> Mit solchen Restriktionen können Felder mit Zahlen oder Zeichenketten ausgezeichnet werden. Mit dem evaluator-Attribut wird angegeben, wie der jeweilige Wert des Feldes auzuwerten oder zu interpretieren ist.
  4. Um solche Feld-Restriktionen können nun noch logische Verknüpfungen (<and-constraint-connective/>, <or-restriction-connective/>) geschachtelt werden.
  5. Um die Patterns können weiterhin Negationen (<not/>), der Existenz- (<exists/>) und der All-Quantor (<forall/>) geschachtelt werden.
  6. Neben den Patterns zur Beschreibung der Beschaffenheit eines Objekts gibt es noch die Möglichkeit, diese durch eigenen Java-Code zu prüfen. Dafür wird der Tag <eval></eval> benutzt. In ihm kann beliebige Logik formuliert sein, die weitere Objektmerkmale prüfen kann. Die einzige Einschränkung ist, dass der Ausdruck zu einem Boolschen Wert evaluiert.
Der Konsequenzteil:
  1. Im Konsequenzteil der Regel kann nun jedweder Java-Code stehen. Entsprechende Importe sind dafür nicht zu vergessen. 
  2. Um auf das aktuelle Objekt zuzugreifen und die Konsequenz für ein solches auszuführen, benötigt man eine Referenz darauf. Diese konnte durch die angabe eines Bezeichners (identifier) im Pattern angelegt werden. Damit haben wir nun Zugriff auf die Daten des Objekts. Für ein besseres Verständnis sorgt es, wenn in den Regelbedingungen angelegte Bezeichner mit beispielsweise dem Dollar-Zeichen den Unterschied zu im Java-Code definierten Bezeichnern singnalisieren.
  3. Typisch für XML ist der Overhead der durch die Dokumentstruktur entsteht. Aus diesem Grund, und für die Wiederverwendbarkeit ist es sinnvoll, Logik zu kapseln. Einfacherweise macht man dies in Java-Klassen, die nur eingebunden und aufgerufen werden brauchen. Eine zweite Möglichkeit ist die Verwendung von Dokument-Internen Funktionen. Ein Exemplar davon ist genau unter den Import-Beschreibungen des Beispieldokuments zu finden. Weiterer Erklärung bedarf es nach all den Erläuterungen nicht. Wie zu sehen kann im Funktionsrumpf wieder Java-Code stehen. Funktionen sind vor allem dafür geeignet sehr Regelspezifische Logik im Regeldokument zu kapseln. Logik, die verwehrte Anwendung in unterschiedlichen Regeldokumenten gebraucht wird, sollte besser in Javaklassen verpackt werden.
Nachdem nun die Syntax des Regeldokuments recht ausgiebig erklärt wurde, folgt nun eine kurze Beschreibung der Semantik. Was soll die Regel tun?

Für jede Task-Instanz, die noch nicht im Zustand beendet (COMPLETED) oder fehlgeschlagen (FAILED) ist, und deren Bearbeitungsfrist bereits zur Hälfte überschritten wurde, soll eine Konsequenz ausgeführt werden.
Sinn der Regel ist, das Scheitern des Human Tasks zu verhindern. Darum lautet die Konsequenzhandlung: Es muss eine zuständige Person, z.B. ein Abteilungsleiter, per E-Mail über den dringenden Zustand informiert werden.

Alle Sprachelemente und Funktionen konnten natürlich nicht gezeigt werden, jedoch wurde in diesem Post eine kleine Einführung in Drools-XML-Regeln gegeben.