Heiner KückerState Transition Engine |
|||||||||||||||||||||||||||||||||||||||
Home Java-Seite Bit Packed Array ASM Improved heterogene Map, HMap Constraint Code Generator JSP WorkFlow PageFlow FlowControl Page Flow Engine Web Flow Engine Control_and_Command JSP_Spreadsheet Code-Generator für Option-Either-Stil in Java verbesserter Comparator Fluent-Interface Code-Generator auf Basis einer Grammatik Visitor mit Multidispatch for-Schleife mit yield-return Kognitions-Maschine semantisches Netz Domain Parser Codegenerator_für hierarchische Datenstrukturen Expression_Engine Formula_Parser Thread Preprocessor State Transition Engine AspectJ Java_Explorer DBF_Library Kalender_Applet SetGetGen BeanSetGet CheckPackage LineNumbers GradDms Excel-Export StringTokenizer JspDoc JspCheck JSP-Schulung Java Server Pages Struts Ascii-Tabellen- Layouter Ascii-Baum- Layouter Ascii-Art-Fluss- Diagramm- Parser AsciiArt AssignmentMatrix Layouter StringSerial Silbentrennung JDBC_Schlüssel- Generierung bidirektional/ unidirektional gelinkte Liste Java_Sitemap Generator XmlBuilder RangeMap StringFormatter VersionSafe XCopy JTextField CommandLine- ParamReader Bitmap-Grafik MultiMarkable- Buffered- InputStream JavaCache JdomUtil CollectionUtil XML Really Pull Parser Log-Filter Remote-Protokoll Sudoku-Generator Delegation statt Mehrfachvererbung Disjunct Interval Set WebCam_Demo Weiterentwicklung_Java Alaska-XBase++-Seite Projekte Philosophien Techniken Konzepte Sudoku Kontakt / Impressum Links SiteMap Letzte Aktualisierung: 25.08.2003 |
Anleitung StateTransitionEngineDie StateTransitionEngine (Zustands-Übergangs-Maschine) stellt eine Lösung zur Realisierung von Workflows (Fluss-Steuerungen) oder von zustandsbehafteten Systemen dar. Entsprechende grafische Modelle sind Aktivitätsdiagramme oder Petri-Netze.+-----------+ +-----------+ +->| Zustand 1 |----------->| Zustand 2 |--+ | +-----------+ +-----------+ | | | | +-----------+ +-----------+ | +--| Zustand 4 |<-----------| Zustand 3 |<-+ +-----------+ +-----------+Die Struktur eines Zustandsübergangsdiagrammes wird in eine Tabellendarstellung transformiert. Die Steuertabelle hat folgenden Aufbau:
Das heisst also, dass zu jedem Status eine Tabelle mit möglichen Ereignissen und zu jedem Ereignis eine Tabelle mit Bedingungen, entsprechenden Ziel-Zuständen und Aktionen (Methoden-Aufrufe) vor und nach dem Wechsel exisitiert. Jeder Zielzustand mit den dazugehörigen Bedingungen hat eine bestimmte Position in der Reihenfolge der Zielzustände je Ereignis, um die Reihenfolge beim Bestimmen des passenden Zielzustandes eindeutig festzulegen. In der Implementierung wird die Status-Tabelle auf einen Baum mit folgender Struktur abgebildet. StateTransitionEngine | +--> SourceState (Map) | +--> StateTransitEvent (Map) | +--> DestinationState (List) , isUserPermitted() , isTransitConditionTrue()Eine wichtige Regel beim Entwurf der Zustands-Übergangs-Tabelle ist, dass StateTransitEvents stets Imperative sein sollten: Gib in Bearbeitung! Gib frei! Setze auf Status "Geprüft"! Weise zurück! Lösche! während ein Status immer einen Zustand beschreibt: Ist in Bearbeitung. Ist Freigegeben. Wurde Geprüft. Ist Zurückgewiesen. Ist Gelöscht. Prinzipiell könnte man die StateTransitEvents auch StateTransitActions nennen. Ich habe mich jetzt erst mal auf den Namen Event festgelegt. Wahrscheinlich spielt es keine Rolle. Beim Entwurf der StateTransitionMap sollte das einem aber bewusst sein. Wenn man für die States (Source und Destiantion) und die Events kryptische (technische) Namen verwendet, bietet es sich an, vor der Anzeige auf der GUI für Statusinformation und menüartige Event-Auswahl (Selectbox, Links, Buttons) eine Internationalisierungs-Logik (i18n) mit RessourceBundles dazwischenzuschalten. Dadurch erhält man neben der Internationalisierung noch eine Übersetzung von technischer zu fachlicher Notation. Das Definieren/Erzeugen der Zustands-TabelleZuerst wird eine StateTransitionEngine erzeugt:// create engine StateTransitionEngine stateTransEng = new StateTransitionEngine();Dieser StateTransitionEngine können beliebig viele Zustände zugeordnet werden. Da diese Zustände Anfangspunkte von Zustandsübergängen sind, nennen sie sich SourceState. SourceState stateStart = new SourceState("Start"); stateTransEng.add(stateStart);Jedem SourceState können beliebig viele Ereignisse (StateTransitEvent) zum Zustandsübergang zugeordnet werden. Unterschiedliche Ereignisse können dabei unterschiedliche Zielzustände spezifizieren. +-------------+ | SourceState | +-------------+ | +--------------------+ +--- StateTransitEvent 1 ---->| DestinationState 1 | | +--------------------+ | | +--------------------+ +--- StateTransitEvent 2 ---->| DestinationState 2 | +--------------------+ // create from state start event next StateTransitEvent nextFromStateStartEvent = new StateTransitEvent("next"); stateStart.add(nextFromStateStartEvent);Jedem Zustands-Übergangs-Ereignis (StateTransitEvent) kann mindestens ein Zielzustand zugeordnet werden. // create destination state start to second DestinationState secondDestinationState = new DestinationState("Second"); nextFromStateStartEvent.add(secondDestinationState);Alternativ ist es möglich, jedem Zustands-Übergangs-Ereignis (StateTransitEvent) beliebig viele Zielzustände zuzuordnen, die sich durch ihre Übergangs- Bedingungen (Methode boolean isTransitConditionTrue() ) unterscheiden sollten. Dazu kann eine eigene Klasse von der Klasse DestinationState abgeleitet werden, um die Methoden isTransitConditionTrue, transitConditionDescription, beforeStateChanged und afterStateChanged zu überschreiben. // create destination state start to second // Die eigene (Anwender-)Klasse SecondDestinationState // erbt von DestinationState DestinationState secondDestinationState = new SecondDestinationState(); nextFromStateStartEvent.add(secondDestinationState);Die mit einenm StateTransitEvent verbundenen DestinationState´s werden in der Reihenfolge, in welcher sie aufaddiert wurden (Methode add oder Reihenfolge in der XML-Datei) nach dem Erfüllen der Umschalt-Bedingung (isTransitConditionTrue()-Methode) abgesucht. Der erste passende DestinationState wird angesprungen. Dabei werden die Aktions-Methoden beforeStateChanged vor und afterStateChanged nach dem Umschalten des Status ausgeführt. Durch den Einbau der User-Rollen-basierten Bedingungsabfrage werden diese in die Prüfung, ob der Zielzustand passt, mit einbezogen. Nach dem kompletten Aufbau der Zustands-Übergangs-Tabelle sollte ihre innere Struktur geprüft werden. Dabei wird geprüft, ob zu jedem DestinationState ein passender SourceState existiert. Der Einbau weiterer Prüfungen ist vorgesehen, zum Beispiel, ob jeder SourceState, ausser initial, über einen DestinationState erreicht werden kann. Beim Einlesen aus einer XML-Datei ist der check()-Aufruf nicht unbedingt nötig, er wird automatisch erledigt. // check validity of state map if (!stateTransEng.check()) { System.out.println("state map is invalid"); System.out.println("" + stateTransEng.getCheckMessages()); System.exit(0); oder throw new StateTransitionEngineIsInvalidException(); } else { System.out.println("state map is valid"); } Anschliessend wird mindestens ein State-Objekt mit Bezug auf die jetzt fertige StateTransitionEngine erzeugt. // create application state ApplicationState appState = new ApplicationState(stateTransEng) ;Durch das Abschicken der Events kann der Status weitergeschaltet werden. appState.perform("next", ctx);//ohne User-Rolle appState.perform("next", ctx, strUserRole);//mit User-RolleAls Kontext kann eine beliebiges Objekt, zum Beispiel auch this übergeben werden. Dieses Objekt wird in die anwendungsorientierte Prüfung der Umschaltbedingungen einbezogen. Hier der entsprechende Code aus SecondSecondDestinationState: /** * check condition with overgiven context method to implements by the user of the StateTransitionEngine */ public boolean isTransitConditionTrue(Object paObjStateContext) { Context contxt = (Context) paObjStateContext; if (contxt == null) { System.out.println("overgiven context is null"); return true; } // perform if Context.i <= 3 return contxt.i < 3; }// end method Generieren aus XML-FileEine XML-Datei kann als InputStream mit der Methode readXmlInputStream aus dem gleichen Verzeichnis wie die Source-Klassen, bzw. aus einem jar-File eingelesen werden.InputStream xmlInpStream = stateTransEng.getClass().getResourceAsStream( "/de/cnc/statetransitiondemo/DemoStateTransitionMap.xml"); stateTransEng.readXmlInputStream(xmlInpStream); stateTransEng.check();//bei Einlesen aus XML nicht unbedingt nötig, wird automatisch erledigtAls XML-Reader dient die Open-Source-Library kXml. Deshalb muss die mitgelieferte kxml.jar in den CLASSPATH eingebunden werden. Hier ist die XML-Definitionsdatei für die oben als Beispiel angegebene Zustands-Übergangs-Tabelle, die auch der aufgebauten Zustands-Übergangs-Tabelle aus StateDemo.java entspricht. siehe hierzu die mitgelieferte Beispieldatei /statetransitiondemo/DemoStateTransitionMap.xml Tags -StateTransitionMap äusseres Tag Attribute: checkUserRole (true/false), optional, schaltet checkUserRole ein/aus darf folgendes Tag enthalten: SourceState -SourceState darf nur im Tag StateTransitionMap erscheinen Attribute: Name Pflicht-Attribut initial (true/false), optional, setzt den State als Anfangszustand darf folgendes Tag enthalten: StateTransitEvent -StateTransitEvent darf nur im Tag SourceState erscheinen Attribute: Name Pflicht-Attribut darf folgendes Tag enthalten: DestinationState -DestinationState darf nur im Tag StateTransitEvent erscheinen Attribute: Remark optionaler Kommentar DestinationStateName Pflicht-Attribut, Name des anzusteuernden States AllPermitted optional, erlaubt diesen DestinationState für alle User-/User-Rollen, wenn true (Wenn das Attribut AllPermitted auftaucht, schaltet der XML-Parser den checkUserRole für die StateTransitionEngine ein) UserRole optional, darf beliebig oft wiederholt werden, Angabe einer/mehrerer User-Rollen, für die der anzusteuernde Status erlaubt ist (Wenn das Attribut UserRole auftaucht, schaltet der XML-Parser den checkUserRole für die StateTransitionEngine ein) (eventuell mit kommaseparierter Liste arbeiten) darf folgendes Tag enthalten: keines Einbringen applikationsspezifischen Codes in die Zustands-Tabelle und
Durch Erben von der Klasse DestinationState kann der Benutzer dieser StateTransitionEngine eigenen
Code für die Wechsel-Bedingung (transit condition) sowie die Vor- und Nach-Wechsel-Aktionen
einbringen. Die Möglichkeit zur Verwendung von User-Code schien mir flexibler als eine Realisierung
mit Primitiv-Bedingungen, zu interpretierenden Ausdrücken oder Reflection-Features.
|