500 likes | 688 Views
Gliederung: Teil 3 - Anpassungen. 1. Einführung Motivation Definition, Konzepte für Komponenten (klassische, kommerzielle, akademische) 2. Industrielle Komponentensysteme der 1. Generation 1. CORBA 2. Enterprise Java Beans 3. (D)COM 3. Anpassungen Daten und Funktion Interaktion
E N D
Gliederung: Teil 3 - Anpassungen 1. Einführung • Motivation • Definition, • Konzepte für Komponenten (klassische, kommerzielle, akademische) 2. Industrielle Komponentensysteme der 1. Generation 1. CORBA 2. Enterprise JavaBeans 3. (D)COM 3. Anpassungen • Daten und Funktion • Interaktion • Kommunikation • Synchronisation • Protokolle • Lebendigkeit
Problem • Komponenten mit Zustand • Aufruf der Methoden (Dienste) bewirkt Zustandstransformationen • Nicht jeder Dienst kann in jedem Zustand erbracht werden • Protokoll beschreibt Folgen möglicher „Dienstleistungen“ einer Komponente
Beispiele • Komponente „Datei“:open (read | write )* close • Komponente „Keller“: (pushn popn)* • Thermostat:(zu kalt | zu heiss)* • Telefonleitung: Abheben ( Ziffer* ( Aufgelegt | Timeout | NummerKorrekt ( besetzt | klingelt ( Aufgelegt | Angenommen Sprachdaten*(Aufgelegt|RemoteAufgelegt) ))))*
Definitionen • Protokoll: • Menge von Folgen von Methodenaufrufen • Formale Sprache mit Methoden als Alphabet • Komponentenprotokoll K • Protokoll (Sprache), das legale Aufruffolgen von Methoden einer Komponente definiert • Aufrufprotokoll A • Protokoll, das die tatsächliche Verwendung einer Komponente definiert
Korrekte Verwendung • Sei prefix(K) die Menge aller Methodenfolgen, die Anfang eines Satzes aus K sind. • Komponente wird korrekt verwendet, wenn A prefix(K) oder A K • Frage, ob Endzustand erreicht werden muss • Was gelten muss, hängt von der Komponente ab (Datei vs. Keller)
Anpassung • Wrapper um die Komponente • Generiert notwendige Methoden • Konsumiert überflüssige Methoden • Blockt Methoden, die ungelegen kommen, dadurch Umsortieren von Methodenaufrufen zur Laufzeit • Insgesamt teuere Operationen • Was kann bereits statisch erkannt werden?
... File f=new File(); File g=new File(); f=g; f.open(...); g.open(...); f.write(...); g.write(...); ... File g ist „Alias“ für File f Um festzustellen, ob Komponenten richtig verwendet werden, müssen „Aliases“ gefunden werden Problem
... File f=new File(); File g=new File(); if someFunc() f=g; f.open(...); g.open(...); f.write(...); g.write(...); ... File g ist vielleicht „Alias“ für File f Finden von „Aliases“ ist unentscheidbar Ergebnis von someFunc() müsste statisch geraten werden können Problem
Aliasproblem • Komponente kann durch mehrere Ausdrücken (Zugriffspfaden) referenziert werden (alias) • Komponentenprotokoll kann durch alle Aliases fortgeschaltet werden • Finden von Aliases ist unentscheidbar • Lösung • Points-To Analyse • Typsplitting
Points-To Analyse • Datenflussanalyse • Datenstruktur ist • Steuerflussgraph • SSA Graph des Programms • Jedem Ausdruck im Programm wird Menge von Definitionen zugeordnet • Aktualisierung bei Zuweisung, Prozeduraufruf • May und Must Problem unterschieden • Unterschiedliche Behandlung bei Zusammenflüssen im Steuerflussgraph
Points-To (May) ... File f=new File(); File g=new File(); if someFunc() f=g; f.open(...); g.open(...); f.write(...); g.write(...); ... new f=#1 g=? new f=#1 g=#2 if f=#2 = g=#2 f={#1,#2} g=#2 f={#1,#2} g=#2 f={#1,#2} g=#2 f={#1,#2} g=#2
Points-To (Must) ... File f=new File(); File g=new File(); if someFunc() f=g; f.open(...); g.open(...); f.write(...); g.write(...); ... new f= #1 g=? new f= #1 g=#2 if f= #2 = g=#2 f= g=#2 f= g=#2 f= g=#2 f= g=#2
Typsplitting • Jedes Objekt hat Protokoll als Typ • Beim Erzeugen von Aliases wird das Protokoll zwischen beiden Aliasausdrücken aufgeteilt • Es entstehen zwei Sichten auf die Komponente, die zusammen das gesamte Protokoll bilden • Schwierigkeiten • mit may-alias • Zusammenflüssen im Steuerfluss
Beispiel Typsplitting ... File (open, write*, close) f=new File(); init(f); f.write(...); //f ist vom Typ: File (write*, close) ... f.close(...); ... void init (File (open write) g){ g.open(...); g.write(...); }
... loop{ x=new K(); x.init(); x.doSome(); x.close(); } ... new K() steht für eine Menge von Komponenteninstanzen zur Laufzeit Abstrakte Namen zur Bezeichnung in der statischen Analyse Problem
Fabrik(){ K[] container; init(){ loop{ container.add(new K()); } } get(){ return(container.get()); } } x=get(); y=get(); x.init(); y.init(); ... Abstrakte Namen können konkrete Komponenten nicht immer unterscheiden Problem unentscheidbar Problem
Namensschemaproblem • Potentiell beliebige Anzahl von von dynamisch erzeugten Komponenteninstanzen • Muss für statische Analyse zu endlicher Anzahl abstrahiert werden • Analyse muss fehlerhaft sein
Mögliche Namensschemata • Alle (dynamischen) Objekte sind eins • Alle (dynamischen) Objekte gleichen Typs (der gleichen Komponente) zu einem abstrakten zusammenfassen • Alle Objekte, die an der gleichen Stelle erzeugt wurden, zusammenfassen • Alle Objekte, die an der gleichen Stelle erzeugt wurden, wobei diese Erzeugung von der gleichen Prozedur aufgerufen wurde • Letzteres verallgemeinerbar auf Aufrufpfade der Länge k
Problem • Mehrere Klienten greifen auf identische Komponente zu • Klienten laufen parallel aber nicht synchron • Beobachtbares Verhalten der Umgebung ist nicht deterministisch • Reihenfolge der Methodenaufrufe, wie sie an der Komponente ankommen, ist Verzahnung („Interleaving“) der Aufrufe der beteiligten Klienten • Eingeschränkt durch Abhängigkeiten der Klienten untereinander
Problem Nebenläufigkeit • Sei A1 das Aufrufprotokoll von Klient 1 und A2 das Aufrufprotokoll von Klient 2 • Sei f1: m1,m2, ... , mnA1 die Aufruffolge von Klient 1 undf2:m‘1,m‘2, ... , m‘kA2 die Aufruffolge von Klient 2 • An der Komponente kann jede Folge der Bauart:m‘‘1, m‘‘2, ... , m‘‘n+k ankommen mit m‘‘i f1 m‘‘i f2 und Folgen f1 undf2 sind Teilfolgen • Aufrufprotokoll ist Produkt der Sprachen A1A2 • Verallgemeinerung auf k Klienten A1A2 ... Ak • Exponentielles Wachstum: • Der Sprache (nicht so schlimm) • Der Sprachdefinition
S SS | push S pop | S SS | push S pop | S‘ (S|S) (S|S) S push pushS‘pop pop | S push push S‘pop pop | S push S‘ pop | S push S‘ pop | Beispiel Keller
Beispiel: 2 Klienten für eine Datei read/ write open close read/ write open close
Nebenläufige Klienten der Datei read/ write open close read/ write open open open read/ write read/ write read/ write close close close open close read/ write
Legale Aufrufe an eine Datei read/ write open close open open read/ write read/ write close close open close read/ write
Problem • Ein Klient und eine Komponente im System • Komponente ist web-Dienst • Aufrufprotokoll A: z.B. sei f: m1,m2, ... , mnA • Komponentenprotokoll K: z.B. sei g: m1,m2, ... , mnK • Web-Kommunikation ist nicht reihenfolgeerhaltend • An der Komponente kommen die Aufrufe in vertauschter Reihenfolge an, z.B. f‘: m2,m1, ... , mn mit f‘ K • Immer Anpassungen zur Laufzeit notwendig
Statische Analyse • Simulieren der Methodenaufrufe zur Übersetzungszeit • expr.m() erzeugt sog. „Update“ der Menge von Objekten, die expr bezeichnet • Mögliche Aliases – „weak update“ – alter Zustand wird als möglicher aktueller gemerkt • Garantierte Aliases – „strong update“ – alter Zustand wird fortgeschaltet • Aussagen • Garantiert korrekt • Fehler können nicht ausgeschlossen werden • Fehler garantiert
Program Slicing • Aufgabe: lösche Programmstellen, die zu Aufrufen an die Komponente keinen Beitrag leisten • Ziel: konservative Abschätzung • Datenstruktur ist Aufrufgraph oder Steuerflussgraph • Anfang: potentielle Erzeugungen und Aufrufe • Lösche alle Knoten, die nicht auf einem Pfad vom Programmanfang main zur Erzeugung einer Instanz oder zum Aufruf an eine Instanz führen • Ungenau, da in gelöschten Prozeduren berechnete Werte den Steuerfluss zum Aufruf beeinflussen können
Berechnung der Aufrufprotokolls • Definition einer Grammatik • Jeder Methode der Komponente entspricht Terminalsymbol • Jeder anderen Methode entspricht Nichtterminal • main – Satzsymbol • Methodenrümpfe entsprechen rechten Seiten von Produktionen • Schleife: * • Bedingung: | • Methodenaufruf: Terminal oder Nichtterminal
main{ File f=new File(); start(f); } start(File f){ f.open(...); doSome(f); f.close(); } doSome(File f){ loop char c=f.read(); f.write(„bla“); } Main Start Start open DoSome close DoSome read* write Beispiel
Ergebnis • Kontextfreie Grammatik • Definiert Obermenge A‘ des Aufrufprotokolls A AA‘ A‘ K AK • Wie ist K definiert?
Endlicher Automat • K ist durch endlichen Automaten gegeben • Alternativ regulärer Ausdruck oder Grammatik • A‘ Kist unentscheidbar, da A‘ kontextfrei • Lösung: finde reguläre Obermenge A‘‘ von A‘ AA‘A‘‘ A‘‘ K AK • Finden einer möglichst genauen regulären Obermenge einer kontextfreien Sprache?
Reguläre Obermenge kontextfreier Sprachen • Grammatik in Greibach Normalform: • N t • N t N‘ • N N‘N‘‘ • Immer möglich für jede KfG • Konstruiere Endlichen Automaten • Jedem Nichtterminal N werden 2 Zustände zugeordnet: SN (vor Akzeption), S‘N (nach Akzeption) • N t SN t S‘NS‘N SF • N t N‘ SN t SN‘ S‘N‘ S‘N • N N‘N‘‘SN SN‘ S‘N‘ SN‘‘ S‘N‘‘ S‘N
Eigenschaften • Ist regulär (per Konstruktion) • Akzeptiert Obermenge der Sprache • Ist nicht unnötig groß, d.h., wenn die Sprache regulär ist (nur unglücklicher Weise kontextfrei definiert), so wird der Automat konstruiert, der die Sprache exakt akzeptiert
Zustandsexplosion bei Nebenläufigkeit read/ write open close read/ write open open open read/ write read/ write read/ write close close close open close read/ write
Modellprüfung • Ursprung: Formale Logik • Gegeben eine Formel und • ein mathematisches Modell M der Formel • Wird von M erfüllt? • Anwendung auf Software • Formel ist Spezifikation, • Modell M ist Umsetzung: • Erfüllt M seine Spezifikation ? • Ablaufaussagen erfordern Zeitbegriff • Temporales Zeitmodell: (, ) • Quantitatives Zeitmodell: (, , |·|) • für Echtzeitprobleme, • reduzierbar auf temporales Zeitmodell
Erreichbarkeitsanalyse • Aufgabe • Gegeben ein Zustand • Welche Zustände sind erreichbar? • Algorithmus • Beginne mit Z={Start} • Solange z Z, z nicht bearbeitet Z := Z {y | y ist von z direkt erreichbar} • Terminiert, da Zustandsmenge endlich • Anwendung auf Modellprüfung • Gegeben eine Formel und einen Startzustand • Gilt auf allen Ausführungspfaden, auf einem Ausführungspfad ... • Berechne erreichbare Zustände und prüfe !
Grenzen der expliziten Darstellung • Berechnung der Erreichbarkeit ist linear in Größe • des Zustandsraums • der Übergangsrelation • der zu beweisenden Formel • In der Praxis • Systeme sind Komposition paralleler Komponenten • Zustandsraum wächst exponentiell (Zustandsexplosion) • In expliziter Darstellung nicht beherrschbar • Ausweg • Implizite Darstellung mit booleschen Formeln (symbolisch) • Zustandsraum ist n-dimensionaler boolescher Raum • Kodierung als geordnete binäre Entscheidungsdiagramme (OBDD)
Klammersprachen • Dycksprachen • Wohlgeformte Klammerausdrücke über einem Alphabet von Klammern • Beispiel Warenkorb • Paar: Auswahl (X) – Zurücklegen (X) • Ware X beliebig • Inklusionsproblem entscheidbar
Kontextfreie Sprachen • Inklusionsproblem unentscheidbar im allgemeinen • Ansatz: transformiere beide Grammatiken • Konstruiere Greibach Normalform • Eliminiere Kettenproduktionen • Eliminiere nicht erreichbare Produktionen • Eliminiere nicht terminierende produktionen • Vergleiche auf strukturelle Inklusion • Semientscheidbar
Prädikatenlogik • Natürliche Form der Komponentenprotokollspezifikation • Jeder Dienst hat Vorbedingung (pre-condition) • Prädikatenlogische Formel über dem Zustand der Komponenteninstanz und den Parametern • Jeder Dienst garantiert Nachbedingung (post-condition) • Prädikatenlogische Formel über dem Zustand der Komponenteninstanz und den Parametern vor Dienstausführung und über dem Zustand der Komponenteninstanz und dem Resultat nach Dienstausführung • Eigentlich Temporallogik • Nachbedingung impliziert die Gültigkeit von anderen Vorbedingungen
Beispiel class Stack(T) { pre: true; push(T t); post: Stack.count == Stack.count‘ +1 pre: Stack.count != 0; pop(T t); post: Stack.count == Stack.count‘ -1; }
Prädikatenlogik • Statisch unentscheidbar • Beobachtung in der Praxis: • Teil des Zustandes, der relevant ist für korrekte Verwendung ist als Dienst an der Schnittstelle verfügbar • Bsp.: Funktion isEmpty() beim Stack • Lösung zur Laufzeit immer möglich
Guarded Methods • Dienste sind durch Wächter (guards) geschützt • Garantieren, dass kein Dienst aufgerufen wird, wenn die Komponente im falschen Zustand ist • Korrektheit dynamisch gesichert durch Umordnen von Methodenaufrufen • Laufzeitprobleme • Teuer Operationen (Kerneintritt notwendig) • Verwaltung von Methodenwarteschlangen • Lebendigkeit (Verklemmungsfreiheit) offenes Problem
Kriterien • Vollständige Spezifikation des Komponentenverhaltens? • Automatische Tests auf Konsistenz? • Verständlichkeit / Effizienz der Spezifikation? • Effizienz der Ausführung?
Zusammenfassung • Alias-Problem (unentscheidbar) • Abstrakte Namen finden (exakt sein unmöglich, exponentielles Wachstum der Datenstruktur mit der Genauigkeit) • Aufrufprotokoll Finden (unentscheidbar auch ohne Alias- und Namensproblem) • Nebenläufige Aufrufer (exponentieles Wachstum möglicher Sequenzen) • Kanäle, die die Ordnung nicht erhalten (exponentieles Wachstum möglicher Sequenzen) • Sprachinklusion (unentscheidbar oberhalb von regulären Sprachen)
Warum geht alles gut • Komponenten in der Praxis haben einfache Schnittstellen (sonst können die Dienste nicht verwendet werden) • Einfache Protokolle • Komponenten in der Praxis grobgranular (sonst macht Wiederverwendung keinen Sinn) • Wenige Instanzen • Hierarchischer Aufbau von Systemen • Aufrufer in lokalen Subsystemen zu finden • Gegenstand aktueller Forschung – richtige Mischung von statischen und dynamischen Techniken wird gesucht
Best practice • Garantiere Korrektheit durch guards • Analysiere (statisch), welche guards wegfallen dürfen • Analysiere (statisch oder dynamisch), ob System lebendig • Verklemmungsvermeidung (statisch) • Verklemmungsentdeckung (dynamisch) • Verweben von guards und Komponenten