• Keine Ergebnisse gefunden

Reparatur von Programmen mithilfe der Object Constraint Language

N/A
N/A
Protected

Academic year: 2022

Aktie "Reparatur von Programmen mithilfe der Object Constraint Language"

Copied!
314
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

deposit_hagen

Publikationsserver der Universitätsbibliothek

Reparatur von Programmen mithilfe der Object Constraint Language

Mathematik und

Informatik

Dissertation

(2)

R P

O C L

Dissertation

zur Erlangung des akademischen Grades des Doktors der Naturwissenschaften

(Dr. rer. nat.)

der Fakultät für Mathematik und Informatik der FernUniversität in Hagen

vorgelegt von Bastian Ulke

(3)
(4)

Gängige Entwicklungsumgebungen (engl. Integrated Development Environments, IDEs) verfü- gen über ausgereifte Möglichkeiten, Fehler in Programmen einer Programmiersprache wie etwa Java (einerGeneral Purpose Language, GPL) zu erkennen und zu korrigieren. Für viele Auf- gaben, die unabhängig von einer spezifischen Anwendungsdomäne sind, liefern Frameworks heute jedoch standardisierte, wiederverwendbare Lösungen. DieJava Persistence API (JPA), die eine Schni stelle zur Abbildung der Anwendungsobjekte auf Datensä e einer relationalen Datenbank definiert, ist hierfür ein typisches Beispiel. Während IDEs genaue Kenntnis der Wohlgeformtheitsregeln der GPL haben (sodass sie in einem Programm Fehler erkennen und Korrekturen vorschlagen können), sind ihnen die Regeln, an die das Programm bei Nu ung eines Frameworks gebunden ist, zumeist unbekannt. Bei der Implementierung werden Feh- ler daher schnell übersehen, sodass der Entwickler erst auf sie aufmerksam wird, wenn es zu einem Laufzeitfehler gekommen ist.

Gleichwohl könnte eine statische Code-Analyse viele Fehler bereits während der Entwick- lungszeit aufdecken, sodass man von einer IDE erwarten darf, dass sie ebenso bei der Einhal- tung der Framework-spezifischen Regeln Unterstü ung bietet, wie sie es bei denen der GPL tut. Dieser Dissertation liegt die Annahme zugrunde, dass sich an der eingeschränkten Unter- stü ung bei der Verwendung eines Frameworks nichts ändert, solange kein Weg gefunden wird, die Verantwortlichkeiten zur Definition der Wohlgeformtheitsregeln eines Frameworks (die dem Anbieter des Frameworks zukommt) und zur Implementierung der Algorithmen zur Validierung und Korrektur (die dem Anbieter der IDE zukommt) voneinander zu trennen.

Diese Arbeit zeigt, dassConstraint-basierte Entwicklungswerkzeuge, die ein Problem der Soft- wareentwicklung auf den Formalismus einesConstraint Satisfaction Problems (CSP)abbilden, die Grundlage zu dieser Verteilung der Verantwortlichkeiten bilden können. Solche Werkzeu- ge basieren auf der deklarativen Definition einer bestimmten Programmeigenschaft (etwa der Wohlgeformtheit) sowie der Suche nach Programmtransformationen, die dieser genügen; zur Suche können hierbei Algorithmen der künstlichen Intelligenz zur Anwendung kommen.

Diese Ausarbeitung präsentiert (a) einen Sa von Wohlgeformtheitsregeln für die Java Per- sistence API, die als Invarianten in derObject Constraint Language (OCL)angegeben werden;

(b) ein Verfahren zur Überse ung von OCL-Ausdrücken in Constraints eines CSP; (c) einen Algorithmus zur Bestimmung und Lokalisierung von Fehlern auf der Grundlage dieser Con- straints; (d) eine Methode zur Bestimmung von Korrekturvorschlägen zu einem Fehler, das Shallow Fixing, bei dem ein Fehler in Isolation betrachtet wird und hiebei – wie es auch die Quick Fixesgängiger IDEs tun – die Gefahr birgt, neue Fehler in ein Programm einzubringen, und (e) drei Ansä e, die dieses Risiko eliminieren, indem sie weitere Constraints bei der Su- che nach Korrekturvorschlägen mit einbeziehen, wasDeep Fixinggenannt wird. Schließlich werden diese Ansä e (f) auf der Grundlage von Open-Source-Projekten evaluiert. Die Er- gebnisse zeigen, dass der präsentierte Regelsa ernsthafte Implementierungsfehler aufdecken kann und die präsentierten Lösungsansä e zu ihrer Reparatur geeignet sind.

(5)
(6)

Contemporaryintegrated development environments (IDEs)elaborate functionality with regard to error detection and error fixing for programs ofgeneral purpose languages (GPLs)such as Java.

However, for many domain-independent concerns, frameworks nowadays deliver standardi- zed, reusable solutions. TheJava Persistence API (JPA)is a typical example of this, as it specifies a standardized API for the mapping of application objects to records of a relational database and vice versa. While the GPL’s rules of well-formedness are well-known to IDEs (enabling them to detect errors early and to suggest program transformations for their repair), the rules that the used frameworks impose on programs are not. Consequently, programmers experi- ence costly interruptions as they easily overlook errors and do not become aware of them until the application fails at runtime.

On the other hand, a static code analysis could reveal many errors at development time and one may expect that an IDE provides support for framework-specific well-formedness rules to the same extent as it does for the underlying GPL. This dissertation argues that the limitation of framework support cannot be overcome unless a way is found to separate the responsibility of defining the rules of a framework’s well-formedness (which is up to the framework provider) from that of implementing algorithms for their validation and fixing detected errors (which is up to the IDE provider).

This work shows thatconstraint-based development tools, that is, mapping a software develop- ment problem to the well-understood formalism of aconstraint satisfaction problem (CSP), can provide the fundamentals for that distribution of responsibilities. These tools are based on a declarative definition of a specific program property (e.g., well-formedness) and the search of program transformations satisfying it by using generic search algorithms from artificial intel- ligence.

This thesis presents (a) a set of well-formedness rules for the Java Persistence API which is given as invariants in the Object Constraint Language (OCL); (b) a procedure translating OCL expressions into constraints of a CSP; (c) an algorithm for error identification and localizati- on based on these constraints; (d) a method for the determination of repair suggestions for a given error, calledshallow fixing, that considers an error in isolation and — just likequick fixes of typical IDEs do — risks introducing new errors into the program and (e) three different approaches eliminating that risk by including related constraints during the search for repair suggestions, which is calleddeep fixing. Finally, these approaches are (f) systematically evalua- ted on open source projects. Results show that the presented ruleset for JPA reveals serious implementation errors and that the correction methods are well-suited for their repair.

(7)
(8)

Zu allererst danke ich Friedrich Steimann für die Mühe und Geduld, die er in den vergangenen Jahren aufgebracht hat, um mir das wissenschaftliche Arbeiten näher zu bringen. Dass seine Bürotür jederzeit für mich offen stand, um Fragen und Probleme bei der Verfassung dieser Arbeit zu diskutieren, hat diese überhaupt erst möglich gemacht.

Jörg Keller danke ich für seine Bereitschaft, mit dieser Dissertation nun auch meine drit- te wissenschaftliche Ausarbeitung zu begutachten. Seine vielen Hinweise zum Schließen von inhaltlichen Lücken und zum Ausräumen von Missverständlichkeiten haben einen sehr wert- vollen Beitrag zu ihrer Finalisierung geleistet.

Dafür, dass Sie sofort bereit waren, die Promotionskommission zu vervollständigen, danke ich weiterhin Jörg Haake und Hauke Col au.

Ein ganz besonderer Dank gilt auch Daniela Keller, die – als die erste Fassung dieser Arbeit fertig gestellt war – alles hat stehen und liegen lassen, um mit einer überwältigenden Geduld und Zuverlässigkeit jeden Tippfehler im Manuskript aufzuspüren und anzustreichen.

Andreas Thies, Marcus Frenkel und Jörg Hagemann schulde ich ein großes Dankeschön für ihre geduldigen Erklärungen und die vielen inspirierenden Diskussionen, die mir sehr oft dabei geholfen haben, Probleme und Erkenntnisse zu ergründen und den Blick auf das Wesentliche zu richten.

Herrn Steimann, Daniela, Andreas, Marcus, Jörg und ebenso auch Sarah Lang, Angelika Steffens und Andrea Frank möchte ich vor allem aber dafür danken, dass sie mir vor gut viereinhalb Jahren ein warmes Willkommen an der FernUni bereitet haben. Ich hä e diese Arbeit nicht schreiben können, hä en unsere vielen persönliche Gespräche und Späße mir über die le ten Jahre nicht von Zeit zu Zeit zu ein paar Minuten

”Urlaub“ verholfen. Ihr werdet mir fehlen, wenn ich eines Tages aus dem Dienst der FernUni ausscheide...

Meiner Freundin Lina Schlüter danke ich dafür, dass sie in all der Zeit nicht müde gewor- den ist, mich aufzumuntern und zum Fortfahren anzuhalten. Tro aller Entbehrungen, die ich ihr (vor allem in den le ten anderthalb Jahren) abverlangt habe, hat sie mir geduldig bei- gestanden und mir mit ihrer Zuversicht den Rücken gestärkt.

Auch meiner lieben Familie gebührt Dank für ihr unerschü erliches Vertrauen und für den Beistand, der mich durch alle Höhen und Tiefen des Promotionsvorhabens begleitet hat.

Herzlichen Dank euch allen – ihr wart großartig!

(9)

Berichtersta er:

Prof. Dr. Friedrich Steimann Prof. Dr. Jörg Keller

(10)

1. Einleitung 11

2. Problemstellung 13

. . Einführung in JPA . . . . . . Mangelnde Framework-Unterstü ung durch IDEs . . . . . . Ein Erklärungsansa und eine Vision . . . . . . Constraint-basierte Entwicklungswerkzeuge . . . . . . Die Object Constraint Language (OCL) . . . . . . Eigener Beitrag . . . . . . Au au der Arbeit . . . .

3. Verwandte Arbeiten 37

. . Die Java Persistence API . . . . . . Pluggable Type Systems und Annotation Processing . . . . . . Constraint-basierte Entwicklungswerkzeuge . . . . . . OCL als Sprache zur Constraint-Erzeugung . . . . . . Modell- und Programmvalidierung und -reparatur . . . . . . . Bestimmung von Fehlern . . . . . . . Fehlerkorrektur . . . . . . Einordnung der vorliegenden Arbeit . . . .

4. Ein Regelsatz für JPA 73

. . Zugrundeliegendes Metamodell . . . . . . . Elemente eines Java-Programms . . . . . . . JPA-spezifische A ribute und Elemente . . . . . . Regelsa . . . . . . . Invarianten für abgebildete Typen . . . . . . . Invarianten zur Definition des Primärschlüssels . . . . . . . Invarianten für persistente Felder . . . . . . . Invarianten für Aufrufe des Entity Manager . . . .

5. Validierung und Korrektur 109

. . Überse ung von OCL-Invarianten und -Ableitungsregeln . . . . . . . Die Intermediate Constraint Language (ICL) . . . . . . . Erzeugung eines ICL-Constraints aus einer OCL-Invariante . . . . . . . Veränderlichkeit in Indirektionsausdrücken . . . . . . . Ein (optimiertes) Überse ungsschema für die Gleichheitsoperation . . . . . . Überse ungsschemata für Mengen und Quantoren . . . . . . . Überse ung von Ableitungsregeln . . . . . . . Boolesche Vereinfachungen . . . . . . . Abschließendes Beispiel . . . . . . . Einschränkungen . . . .

(11)

. . . Zusammenfassung . . . . . . Validierung von Modellen und Identifizierung von Fehlern . . . . . . . Modellvalidierung als Constraint Satisfaction Problem . . . . . . . Das Problem der Fehlerlokalisierung . . . . . . . Ein Verfahren zur Bestimmung von Modellfehlern . . . . . . . Lösung derCSPt(i) . . . . . . . Partielle Ordnung auf Constraints der JPA-Wohlgeformtheitsregeln . . . . Korrektur von Modellfehlern . . . . . . Isolierte Korrektur von Fehlern durch Shallow Fixing . . . . . . . Das Problem der flachen Repräsentation von Annotationsa ributen . . . . . Das Problem der mangelnden Lokalität . . . . . . . Algorithmus zur Bestimmung des CSP für einen Shallow Fix . . . . . . Sichere Korrektur von Fehlern durch Deep Fixing . . . . . . . Deep Fixing als Nachbarschaftssuche . . . . . . . Finden weiterer Reparaturalternativen . . . . . . . Nicht lösbare Fehler . . . . . . . Frühe Erkennung von Unlösbarkeit . . . . . . . Präsentation der Reparaturalternativen . . . . . . . Das Verfahren zur Korrektur eines Fehlers durch Deep Fixing . . . . . . Varianten des Deep Fixing-Verfahrens . . . . . . . Konzentrisches Deep Fixing mit Framework-spezifischer Priorisierung . . . . Deep Fixing mit kombinierter Breiten- und Tiefensuche . . . . . . Beschränkung der Suche . . . .

6. Evaluation 205

. . Implementierungsaspekte . . . . . . Anwendung auf das Fallbeispiel aus Kapitel . . . . . . Die Probanden . . . . . . Identifizierte Verle ungen der JPA-Wohlgeformtheit . . . . . . . Einordnung . . . . . . . Analyse der erkannten Verle ungen . . . . . . Korrektur der identifizierten Verle ungen . . . . . . . Korrektur durch Shallow Fixing . . . . . . . Korrektur durch Deep Fixing . . . . . . Korrektur von injizierten Verle ungen . . . . . . . Korrektur durch Shallow Fixing . . . . . . . Korrektur durch Deep Fixing . . . . . . Beantwortung der Forschungsfragen . . . .

7. Ausblick 269

8. Zusammenfassung 281

A. JPA-Regelsatz 283

B. Grammatik der ICL 297

Literatur 299

(12)

Bevor ich im November meine Beschäftigung als wissenschaftlicher Mitarbeiter am Lehr- gebiet für Programmiersysteme antrat, ha e ich einige Jahre lang Web-basierte Informations- systeme für ein mi elständisches Unternehmen entwickelt. Unter den vielen Pla formen, die hierzu zur Verfügung standen, war dieJava Platform, Enterprise Edition (Java EE)die für unsere Zwecke am besten geeignete. Für viele typische Aufgaben lieferte sie standardisierte Lösungs- ansä e, die sich in unseren Anwendungen direkt nu en ließen: Das Framework zurContext and Dependency Injection (CDI)stellte die zur Kapselung der Anwendungslogik dienenden Ob- jekte, dieEnterprise Java Beans (EJBs), zur Verfügung und übernahm zusä liche Aufgaben wie etwa die Transaktionsverwaltung. DieJava Server Faces (JSF)erlaubten es etwa, eine Web-Be- nu erschni stelle zu spezifizieren und direkte Verknüpfungen zu den EJBs herzustellen. Und dieJava Persistence API (JPA)machte die transparente Nu ung einer relationalen Datenbank möglich, ohne dass der Entwickler selbst Objekte auf Tupel abbilden oderselect-Statements formulieren musste.

Etwas naiv dachte ich damals:”Eine Java EE-Anwendung wird einem halbwegs geübten Java-Entwickler wohl auch ganz zügig von der Hand gehen.“Doch weit gefehlt... Denn jedes Framework brachte eigenedomänenspezifische Sprachen(engl.Domain-Specific Languages, DSLs) mit – häufig in Form von Annotationen, die gewöhnlichem Java-Code anzufügen waren, oder in Form von eigenen Sprachen wie etwa einem XML-Dialekt zur Beschreibung der Benu er- schni stelle. Und natürlich brachte jede Sprache auch einen Haufen zusä licher, verbindli- cher Regeln mit, die bei der Entwicklung miteinander in Einklang zu bringen waren. Auf viele davon wurden meine Kollegen und ich durch ein fünftägiges Training vorbereitet; weitaus mehr ergaben sich später in der praktischen Nu ung.

Doch die Regeln zu kennen und sie in der täglichen Implementierung zu berücksichtigen, blieben zwei Paar Schuhe. Jeder Programmierer weiß, wie schnell einmal ein Fehler in das Programm eingebaut ist, auf den schon die Meldung der Entwicklungsumgebung(engl.Inte- grated Development Environment, IDE) aufmerksam macht. Erfreulich ist, wenn die IDE dann auch gleich einen Korrekturvorschlag unterbreitet, der nur ausgewählt werden braucht.

Zur Unterstü ung des Programmierers bei der Verwendung eines Frameworks verfügen gängige IDEs jedoch zumeist über gar keine oder bestenfalls unzureichende Funktionalitäten.

Auf eine vorzeitige Fehlermeldung, womöglich gar einen Korrekturvorschlag, war bei einem Framework-spezifischen Fehler daher nicht zu hoffen. Welche Methode blieb nun noch, um Fehler zu erkennen und auszumerzen? Viel zu häufig leider nur der Ansa vonVersuch und Irrtum (engl. Trial and Error): Nach einem Implementierungsschri wurde die Anwendung kompiliert und auf dem Anwendungsserver bereitgestellt. Frühestens während des le ten Schri es, demDeployment, wurden Framework-spezifische Fehler erkannt und die Bereitstel- lung mit einer Fehlermeldung abgebrochen. Mitunter fielen sie jedoch auch erst zur Lauf- zeit auf. Doch das Kompilieren und das Deployment brauchten Zeit – oft wurden uns - sekündige Unterbrechungen abverlangt, bevor uns unser Fehler deutlich wurde.

(13)

Aber Unterbrechungen sind teuer! Die psychologische Forschung hat sich schon in den er Jahren mit ihnen befasst [Zei ; Ovs ]; heute weiß man, dass die Wiederaufnahme einer unterbrochenen Tätigkeit dann besonders schwer fällt, wenn eine komplizierte, aber ver- wandte Aufgabe Grund für die Unterbrechung war [GB ]. Rückblickend erklärt dies, wieso meine Versuche, die Wartezeit mit anderen Implementierungsarbeiten zu füllen, primär mei- ner eigenen Verwirrung dienten. Und auch im Bereich der Mensch-Computer-Interaktion ist der Umgang mit Unterbrechungen in das Interesse wissenschaftlicher Betrachtungen gelangt [HA ]. So zeigen Baily und Konstan, dass sich Unterbrechungen eines Benu ers negativ auf die Zeit zur Lösung einer Aufgabe, die Anzahl der begangenen Fehler und die subjektive Empfindung von Verärgerung [BK ] auswirkt. Das späte Erkennen von Fehlern kostet also gleich doppelt – zum einen aufgrund der (unproduktiven) Wartezeit, zum anderen aufgrund der Konsequenzen, die die Unterbrechung auf die spätere Fortführung der Tätigkeit hat.

Wieso also bemühen sich IDE-Hersteller nicht längst um umfassende, ausgereifte Unter- stü ung für die diversen Frameworks? Eine definitive Antwort auf diese Frage wird auch diese Arbeit schuldig bleiben. Allerdings scheint es wahrscheinlich, dass sie sich angesichts (a) einer große Vielfalt unterschiedlicher Frameworks, (b) unzureichenden Dokumentationen ihrer Wohlgeformtheitsregeln und (c) sehr kurzen Entwicklungszyklen einer wahren Sisy- phosarbeit gegenüber sehen, der sie sich gar nicht erst nachhaltig annehmen möchten.

Vielleicht besteht ein Weg aus diesem Dilemma darin, nicht länger die Entwickler der IDEs allein für die Implementierung der Assistenzfunktionalitäten in die Pflicht zu nehmen, son- dern sich diesem Ziel arbeitsteilig zu nähern. Die Entwickler eines Frameworks kennen seine Regeln am besten – ihnen soll die Verantwortung zukommen, diese Regeln in einer formalen Sprache zu spezifizieren. Im Gegenzug erhalten die IDE-Entwickler die Aufgabe, generische Verfahren in ihr Produkt zu integrieren, die einen beliebigen Regelsa einlesen, um in einem Programm Fehler zu identifizieren und, bei Bedarf, Reparaturvorschläge anzubieten.

In der vorliegenden Arbeit werden die Grundlagen hierzu erarbeitet. Im Zentrum steht eine in der deklarativenObject Constraint Language (OCL)formulierte Spezifikation von Wohlge- formtheitsregeln. Mit ihrer Hilfe werden Validierung und Korrektur eines Programms auf die Spezifikation einesConstraint Satisfaction Problems (CSPs)abgebildet, welches mit wohlbe- kannten Verfahren und Bibliotheken gelöst werden kann. Während ein Programm bei seiner Validierung als unveränderlich aufgefasst wird, werden zur Korrektur eines Fehlers Ände- rungen berechnet, die die Wohlgeformtheit herstellen. Zu le terem werden zwei alternative Ansä e präsentiert. DasShallow Fixingberechnet Reparaturvorschläge in Isolation und birgt die Gefahr, Folgefehler in das Programm einzubringen. DasDeep Fixingeliminiert dieses Ri- siko, indem es von der Änderung betroffene Wohlgeformtheitsregeln bei der Suche berück- sichtigt und eine Korrektur sucht, in der diese erfüllt sind. Zur Präsentation und Evaluation der Verfahren dient ein umfassender Regelsa der Wohlgeformtheitsregeln der Java Persis- tence API. Durch Anwendung auf Open-Source-Projekte werden Regelsa und Verfahren systematisch evaluiert.

(14)

Zunächst soll die Problemstellung dieser Arbeit konkretisiert und einige Grundlagen darge- stellt werden.

Zur Motivation und Illustration der in dieser Arbeit vorgeschlagenen Verfahren dient die Implementierung eines Programms, in dem dieJava Persistence API (JPA)[JPA ] zur Anwen- dung kommt. Hierbei handelt es sich um eine Standardisierung von Frameworks zurobjekt- relationalen Abbildung (engl.Object-Relational Mapping, ORM); sie wird in Abschni . kurz vorgestellt.

Leider ist ein wohlgeformtes Java-Programm nicht gleich auch ein wohlgeformtes JPA-Pro- gramm – vielmehr muss der Programmierer eine Reihe zusä licher Wohlgeformtheitsregeln berücksichtigen, die ihm durch die JPA auferlegt werden. Gängige IDEs bieten ihm dabei je- doch nur sehr mäßige Unterstü ung; Abschni . beleuchtet das hierdurch auftretende Pro- blem einer oft langwierigen Fehlersuche.

An einer Erklärung für das Dilemma unzureichender Framework-Unterstü ung in aktu- ellen Entwicklungsumgebungen versucht sich Abschni . ; ebenso zeigt er eine Vision auf, wie es durch eine Arbeitsteilung zwischen Framework- und IDE-Herstellern gelöst werden könnte.

Um das Wissen über die Wohlgeformtheitsregeln eines Frameworks von den Implemen- tierungen der Validierungs- und Korrekturverfahren zu trennen, bieten dieConstraint-basierte Entwicklungswerkzeugeeine vielversprechende Basis. Ihnen liegt eine einzige, deklarative Spe- zifikation einer bestimmten Programmeigenschaft zugrunde, aus der verschiedene Program- mierwerkzeuge gewonnen werden können. Wie eine deklarative Beschreibung der Regeln der Wohlgeformtheit dazu genu t werden kann, ein Programm zu validieren und gefundene Fehler zu korrigieren, wird in Abschni . kurz dargestellt. Hierbei werden eine Reihe von Forschungszielen hergeleitet, die im Rahmen dieser Arbeit behandelt werden.

Um sie für konkrete Werkzeuge nu en zu können, müssen die Regeln der Wohlgeformt- heit jedoch zunächst einmal in einer Notation spezifiziert werden, die eine maschinengestü - te Weiterverarbeitung erlaubt. Als Sprache eignet sich hierzu die deklarativeObject Constraint Language (OCL)[OCL ], die in Abschni . vorgestellt wird. Durch ihre Nu ung zur Imple- mentierung eines Constraint-basierten Entwicklungswerkzeugs ergibt sich ein weiteres For- schungsziel dieser Arbeit.

Unter diesen Eindrücken erläutert Abschni . den Beitrag der vorliegenden Arbeit. In ihrem Mi elpunkt stehen Verfahren, die die Prüfung und die Korrektur eines Programms auf der Grundlage von Wohlgeformtheitsregeln erlauben, die in OCL deklariert sind. Das Kapitel schließt mit einem Überblick über den weiteren Au au dieser Arbeit.

(15)

1 public classDemo {

2 public static voidmain(String[] args) {

3 EntityManagerFactory emf = Persistence.

4 createEntityManagerFactory(”db”);

5 EntityManager em =

6 emf.createEntityManager();

7 Department department =

8 em.find(Department.class,”sales”);

9 Employee employee =newEmployee();

10 employee.id = 42L;

11 employee.department = department;

12 em.persist(employee);

13 }

14 }

15 @Entity

16 public classDepartment {

17 Long id;

18 @Id

19 String name;

20 @OneToMany(mappedBy=”department”)

21 Collection<Employee> employees;

22 ...

23 }

25 @Entity

26 public classEmployee {

27 @Id

28 Long id;

29 @ManyToOne

30 Department department;

31 ...

32 }

Abbildung . .: Ein einfaches JPA Programm.

2.1. Einführung in JPA

Viele (wenn nicht sogar die meisten) Anwendungssysteme werden heute mit Programmier- sprachen entwickelt, denen das objekt-orientierte Weltbild zugrunde liegt.¹ Gleichwohl do- minieren zur persistenten Speicherung ihrer Daten weiterhin relationale Datenbanksysteme, sodass bei der Implementierung zwei unterschiedliche Paradigmen integriert werden müs- sen. Hierbei treten eine Reihe von Problemen auf, die unter dem BegriffImpedance Mismatch zusammengefasst werden und deren Behandlung einen hohen Implementierungsaufwand nach sich zieht. Um diesen zu reduzieren wurden in der Vergangenheit verschiedene Ansät- ze implementiert, die dieobjekt-relationalen Abbildung(engl.Object-Relational Mapping, ORM) standardisieren.

Für das Java-Ökosystem definiert die Java Persistence API [JPA ] einen Standard für ORM- Frameworks, für den sich mi lerweile verschiedene Implementierungen etabliert haben. Ein Java-Programm, das sich strikt an den JPA-Standard hält, istportierbarund kann mit einem be- liebigenPersistenz-Provider, das heißt einer Implementierung dieser Spezifikation, ausgeführt werden. Neben anderen (vgl. Abschni . ) istHibernate ORM² (kurz nurHibernate) ein in der Praxis häufig verwendeter Persistenz-Provider.³

Abbildung . zeigt, wie ein Persistenz-Provider dazu genu t werden kann, Objekte einer Anwendungsdomäne aus einer relationalen Datenbank zu laden und in ihr zu speichern. Die

¹ Eine detailliertere, durch Literatur untermauerte, Analyse findet sich in Abschni . . Auch das im Folgenden kurz genannteImpedance Mismatchwird hierin weitergehend beleuchtet.

² http://hibernate.org/orm/

³ Wird im Verlauf dieser Arbeit mithilfe eines Hyperlinks eine Ressource desWorld Wide Web (WWW)referen- ziert, so wird auf die Angabe des le ten Aufrufdatums verzichtet, wenn es sich bei ihr um die Startseite der Internetpräsenz einer Organisation oder eines organisationseigenen Projekts handelt.

(16)

Teilmenge der Klassen eines Programms, deren Instanzen in einem gemeinsamen relationalen Datenbankschema abgebildet werden sollen, wird alsPersistenz-Unitbezeichnet und besteht im Wesentlichen aus den Klassen, die die Annotation@Entitytragen.⁴ Zu jeder@Entity-Klasse existiert mindestens eine Tabelle im Schema der relationalen Datenbank, in der Repräsenta- tionen ihrer Instanzen als Datensä e abgelegt werden.

Im Beispiel besteht die Persistenz-Unit aus den KlassenDepartmentundEmployee. Die JPA fordert, dass zu jeder@Entity-Klasse ein Primärschlüssel angegeben werden muss. Hierzu sind im Beispiel die FelderDepartment.nameund Employee.idmit der Annotation@Idannotiert;⁵ ihre Typen,StringundLong, gehören zu einer Menge von wenigen Typen, die zur Deklaration eines Primärschlüssels zugelassen sind.

Sofern nicht anders festgelegt, ist jedes Feld einer@Entity-Klassepersistent; der Persistenz- Provider trägt dafür Sorge, dass ihre Werte in der Datenbank gespeichert werden. Für Felder bestimmter Typen (etwa Javas Primitivtypen oderjava.lang.String) ist dies ohne weitere Maß- nahme möglich; dem Provider sind diese Typen bekannt und er verfügt über vordefinierte Überse ungsregeln, gemäß derer er entsprechende Spalten in der Tabelle vorsieht (etwa eine Spalte vom Typvarcharfür Felder vom TypString) und die zugehörigen Werte speichern und auslesen kann.

Der Typ des FeldesEmployee.departmenthingegen ist eine benu erdefinierte Klasse, für die dem Persistenz-Provider kein vordefiniertes Schema zur Überse ung bekannt ist. Hier erfordert das Framework ergänzende Hinweise, um ein passendes Datenbankschema zu in- itialisieren und Lese- und Schreiboperationen entsprechend zu überse en. So wird durch die

@ManyToOne-Annotation in Zeile festgelegt, dass der Wert des Feldes als Referenz zu einer Instanz einer anderen Klasse interpretiert werden soll, die selbst ebenfalls eine@Entity-Klasse ist. DasManyin@ManyToOnebedeutet, dass verschiedene Instanzen vonEmployeedieselbe Instanz vonDepartmentreferenzieren können. DasToOnelegt fest, dass maximal eine Instanz referenziert werden kann, was mit dem Typen des Feldes in Einklang steht. Um solche Objekt- geflechte in der Datenbank ablegen zu können, sieht der Provider eine Spalte in der Tabelle für Employeevor, deren Werte als Fremdschlüssel zu einem Datensa der Tabelle fürDepartment fungieren und somit einen Zeiger nachbilden.

Auch für die Abbildung des FeldesDepartment.employees(vom TypCollection<Employee>) erfordert der Persistenz-Provider zusä liche Hinweise. Durch die Angabe der@OneToMany- Annotation in Zeile wird das Framework angewiesen, den Wert des Feldes als Behälter- Objekt zu interpretieren und eine Assoziation zu jedem referenzierten Objekt zu speichern.

Das One in @OneToManygibt hier an, dass jede referenzierte Instanz von Employee durch maximal eine Instanz vonDepartmentreferenziert wird.

Hat man eine Vorstellung von der Anwendungsdomäne, so bekommt man schnell den Ein- druck, dass es sich beiEmployee.departmentundDepartment.employeesum die gegenseitigen Enden derselben Assoziation handelt, die nur deswegen unabhängig voneinander deklariert

⁴ Auf einen Verweis auf die betreffenden Stellen der Spezifikation wird in diesem Kapitel verzichtet. Dies wird in Kapitel , in dem ein umfassender Sa von Wohlgeformtheitsregeln erarbeitet wird, nachgeholt.

⁵ Dem aufmerksamen Leser fällt auf, dass in der KlasseDepartmentdie Felddeklaration mit dem Namenname und dem TypenStringzur Definition des Primärschlüssels ausgewählt wurde und nicht etwa diejenige mit dem Namenidund dem numerischen TypenLong. Dies steht jedoch in Einklang mit dem (im Folgenden nä- her diskutierten) Aufruf der MethodeEntityManager.find(.)in Zeile , bei dem die Zeichenke e”sales”als Primärschlüsselwert zum Laden einer Instanz vonDepartmentaus der Datenbank verwendet wird. Diese Ent- wurfsentscheidung dient der Illustration eines Defizits gängiger Entwicklungsumgebungen in Abschni . .

(17)

employee

PK id bigint

FK department_name varchar(255)

department

id bigint

PK name varchar(255)

Abbildung . .: Datenbankschema zum Programm aus Abbildung . .

id name 1 sales

id department_name 42 sales

employee department

Abbildung . .: Tabelleninhalte nach Ausführung des Programms aus Abbildung . .

wurden, weil es in Java (wie auch in anderen Programmiersprachen) an einem Konzept man- gelt, eine bidirektionale Beziehung direkt zu implementieren. Damit der Persistenz-Provider die beiden Assoziationen nicht ebenfalls unabhängig voneinander behandelt (dies hä e zur Folge, dass der Entwickler jede Verknüpfung doppelt eintragen müsste, um sich nicht böse Konsistenzprobleme einzuhandeln), wird die @OneToMany-Annotation um das mappedBy- A ribut ergänzt. Durch diese Angabe wird dieses Feld als inverser Teil derjenigen Assoziation ausgezeichnet, die in der Zielklasse (Employee) durch das Feld mit dem Namen”department”

deklariert wird.

Aus den gegebenen Annotationen kann der Persistenz-Provider nun eine Konfiguration be- stimmen, gemäß der er Instanzen von@Entity-Klassen der Persistenz-Unit auf Datensä e ei- ner Datenbank abbildet und umgekehrt. In der vorliegenden Arbeit wird diese Konfiguration Abbildungsstrategiegenannt. Sie kann (sofern gewünscht) auch dazu genu t werden, ein Da- tenbankschema zu erzeugen. Die Phase, in der die Abbildungsstrategie aus dem (mit JPA- Annotationen angereicherten) Java-Code ermi elt und das Datenbankschema erzeugt wird, wird im Folgenden mitBootstrapping⁶ bezeichnet. Die Persistenz-Provider führen hierbei auch einige Plausibilitätsprüfungen durch – so zum Beispiel, ob zu jeder@Entity-Klasse ein Primär- schlüssel definiert ist, oder ob für jedes persistente Feld eine Konfiguration zur Abbildung seiner Werte auf Spalten der Datenbank gefunden werden kann. Verläuft diese erfolgreich, so ist der Persistenz-Provider anschließend bereit zur Verwendung.

Im Beispiel oben initiiert der Aufruf voncreateEntityManagerFactory(.)in Zeile dermain(.)- Methode das Bootstrapping. In Abbildung . ist das bei Nu ung von Hibernate als Persis- tenz-Provider und PostgreSQL⁷ als relationales Datenbankmanagementsystem erzeugte Da-

⁶ Eine exakte Definition des Begriffs zu finden, gestaltet sich schwierig. In einschlägigen Foren und Online- Enzyklopädien herrscht die Auffassung, dassBootstrappingden Prozess bezeichnet, durch den mit begrenz- ten Mi eln ein komplexeres System gestartet werden kann – etwa beim Starten eines Computers, bei dem das BIOS zunächst einen Bootloader aus einem wohldefinierten Bereich eines Speichermediums lädt und aktiviert, der seinerseits den Startprozess des Betriebssystems initiiert. In ähnlicher Weise dient die Ableitung der Ab- bildungsstrategie aus dem annotierten Java-Code dem Persistenz-Provider dazu, sich selbst für die Abbildung von Objekten der Anwendungsdomäne auf das relationale Datenbankschema (und umgekehrt) zu konfigurie- ren. Erst hierdurch wird das Framework einsa fähig.

https://www.postgresql.org

(18)

tenbankschema in Form eines Entity-Relationship-Diagramm dargestellt.⁸ Die bidirektionale Beziehung zwischen Instanzen vonDepartmentund Employee wird hierin durch die Spalte department_nameder Tabelleemployeerepräsentiert, in der der Primäschlüsselwert der refe- renziertenDepartment-Instanz als Fremdschlüssel gespeichert wird.

Nach dem Bootstrapping kann eine Instanz vonEntityManagerFactorydazu verwendet wer- den, einenEntity-Manager (als Instanz der gleichnamigen Klasse) zu erzeugen (Zeile ), der dem Programmierer als Schni stelle zum Framework dient. In Zeile wird mithilfe der Me- thode find(.) diejenige Instanz der Klasse Department geladen, bei der das Primärschlüssel- feld den Wert ”sales” hat. In den Zeilen bis wird im Anschluss eine neue Instanz von Employeeerzeugt, mit einem Wert für den Primärschlüssel versehen und mit der zuvor gela- denenDepartment-Instanz verknüpft. Indem das neue Objekt der Methodepersist(.)überge- ben wird, wird das Framework schließlich angewiesen, eine Repräsentation in der Datenbank zu speichern. Unter der Annahme, dass ein Eintrag für die Instanz vonDepartmentmit dem Schlüssel”sales”zum Zeitpunkt der Ausführung bereits in der Datenbank existierte, ergeben sich nach Terminierung der main(.)-Methode die in Abbildung . dargestellten Tabellenin- halte.

2.2. Mangelnde Framework-Unterstützung durch IDEs

Leider fallen fehlerfreie Softwaresysteme jedoch nicht vom Himmel, sondern müssen vom Programmierer in mühevoller Arbeit in einen Editor eingegeben werden. In Abbildung . wird eine fehlerhafte Variante des Programms aus Abbildung . dargestellt.⁹ Sie stellt seinen Zustand unmi elbar nach der Implementierung der Java-Klassen dar, noch bevor der Pro- grammierer sie mit den JPA-Annotationen auskleidet. Die Aufrufe des Entity-Managers sind als Bestandteil der Geschäftslogik bereits implementiert.

Es sei zunächst angenommen, dass die Entwicklungsumgebung des Programmierers über keinerlei JPA-Unterstü ung verfügt und der Entwickler die JPA-Annotationen imVersuch und Irrtum-Verfahren ergänzt, also nur solche Fehler singulär betrachtet, auf die er zuvor explizit hingewiesen wurde. Der kritische Leser wird hier vorwerfen, dass die Annahme, dass der Pro- grammierer nicht systematischer vorgeht als per Versuch und Irrtum, sehr pessimistisch und wenig realistisch ist. Tatsächlich darf man erwarten, dass ein Entwickler ohne JPA-Erfahrung nach dem Lesen von Abschni . in der Lage ist, das Programm aus Abbildung . in weniger als den unten dargestellten sieben Anläufen zu korrigieren. In der Praxis sind Programme aber komplexer, der Code länger und die relevanten Stellen in mehreren Ansichten verteilt, was die Erkennung der Fehler selbst bei vollständiger Kenntnis aller relevanter Regeln nachhaltig erschwert – der Autor hat sich selbst zu oft darüber geärgert, auf völlig offensichtliche Fehler erst nach einer Fehlermeldung beim Bootstrapping oder zur Laufzeit aufmerksam geworden zu sein.

⁸ Zur Darstellung der Beziehung zwischen den Tabellen kommt dieMartin-NotationoderKrähenfuß-Notationzur Anwendung. Hier stellt sie dar, dass ein Datensa der Tabelledepartmentmit beliebig vielen Datensä en aus employeeund ein Datensa ausemployeemit höchstens einem Datensa ausdepartmentin Beziehung stehen kann. Das einer Spaltenbeschreibung vorangestelltePKmarkiert den Primärschlüssel; das vorangestellteFK einen Fremdschlüssel.

⁹ Das Erzeugen und Speichern derEmployee-Instanz ist für die weiteren Ausführungen unerheblich und wird hier ausgespart.

(19)

33 public classDemo {

34 public static voidmain(String[] args) {

35 EntityManagerFactory emf =

36 Persistence.createEntityManagerFactory(”db”);

37 EntityManager em =

38 emf.createEntityManager();

39 Department department =

40 em.find(Department.class,”sales”);

41 }

42 }

43 public classDepartment {

44 Long id;

45 String name;

46 Collection<Employee> employees;

47 ...

48 }

50 public classEmployee {

51 Long id;

52 Department department;

53 ...

54 }

Abbildung . .: Eine fehlerhafte Variante des Programms aus Abbildung . ohne JPA-Anno- tationen.

Und so ist der Versuch, das Programm aus Abbildung . unter Verwendung des Persis- tenz-Providers Hibernate auszuführen, der Auftakt für eine sich sieben Mal wiederholende Schleife aus ( ) Analyse der Fehlermeldung, ( ) Fehlerbehebung und ( ) erneuter Ausfüh- rung. Die folgende Ablaufskizze zeigt den jeweils gemeldeten Fehler und die Korrektur durch den Benu er. Das den Fehlern vorangestellte”Bs.Ex.“ steht für ”Bootstrapping Exception“ und markiert einen Fehler, der das Scheitern des Persistenz-Providers bei der Ableitung einer Abbildungsstrategie zur Folge hat;”Rt.Ex.“ bezeichnet einen Fehler, der (nach erfolgreichem Bootstrapping) während der Ausführung der Geschäftslogik auftri . Auf den ersten Fehler wird der Programmierer hingewiesen, sobald das dargestellte Programm versucht auszufüh- ren:

. (Rt.Ex.)org.hibernate.MappingException: Unknown entity: Department

Zur Korrektur wird der KlasseDepartmentdie@Entity-Annotation angefügt.

. (Bs.Ex.)org.hibernate.AnnotationException: No identifier specified for entity: Department Zur Korrektur wird dem FeldDepartment.iddie@Id-Annotation angefügt.

. (Bs.Ex.)org.hibernate.MappingException: Could not determine type for:

java.util.Set, at table: Department, for columns: [Column(employees)]

Zur Korrektur wird dem FeldDepartment.employeesdie@OneToMany-Annotation an- gefügt.

. (Bs.Ex.)org.hibernate.AnnotationException: Use of @OneToMany or @ManyToMany targeting an unmapped class: Department.employees[Employee]

Zur Korrektur wird der KlasseEmployeedie@Entity-Annotation angefügt.

. (Bs.Ex.)org.hibernate.AnnotationException: No identifier specified for entity: Employee Zur Korrektur wird dem FeldEmployee.iddie@Id-Annotation angefügt.

(20)

. (Bs.Ex.)org.hibernate.MappingException: Could not determine type for: Department, at table: Employee, for columns: [org.hibernate.mapping.Column(department)]

Zur Korrektur wird dem FeldEmployee.departmentdie@ManyToOne-Annotation ange- fügt.

. (Rt.Ex.)org.hibernate.TypeMismatchException: Provided id of the wrong type for class Department. Expected: class java.lang.Long, got class java.lang.String

Zur Korrektur wird die@IdAnnotation vonDepartment.idnachDepartment.namever- schoben.

. Erst je t gelingt das JPA-Bootstrapping und auch zur Laufzeit treten keine weiteren Fehler auf.

Bei genauerem Hinsehen ist diese”Odysee“ ernüchternd. Jeder Fehler, sogar jede Korrek- tur, hä e durch eine statische Analyse des Quellcodes vorhergesagt werden können: Dass Departmentmit der@Entity-Annotation versehen werden muss, ist aufgrund des Aufrufs aus Zeile notwendig. Weitere Korrekturen ergeben sich durch Schlussfolgerungen aufgrund der Typen der Felder. Ja selbst, dassDepartment.nameund nicht etwaDepartment.idmit@Id zu annotieren ist, lässt sich aus dem Aufruf aus Zeile schließen.

Jeden Fehler einzeln zu verfolgen und zu korrigieren, ist eindeutig nicht effizient; vielmehr führt das anhaltende

”Ka -und-Maus“-Spiel immer wieder zu kostspieligen Unterbrechun- gen des Arbeitsprozesses – insbesondere, wenn das Starten der Software eher Minuten als Sekunden dauert, wie es beim Deployment einer Anwendung auf einem Application Server der Fall ist. Jeder Programmierer sollte von einer Entwicklungsumgebung erwarten dürfen, bereits zur Entwicklungszeit auf erkennbare Probleme hingewiesen zu werden.

Wie steht es bei gängigen IDEs also um JPA-Support? Um einen groben Eindruck hiervon zu erhalten, wurde die oben beschriebene Odyssee mithilfe der drei prominentesten Vertreter durchgespielt – sie alle beanspruchen für sich, über Unterstü ungsfunktionalitäten für die Entwicklung von JPA-Programmen zu verfügen:

• Eclipe Neon.

In der Ausgabe als”Eclipse Java EE IDE for Web Developers“¹⁰ kann einem Projekt das JPA Facetzugefügt werden. Hierdurch berücksichtigt die IDE bei der Validierung einige JPA-Regeln.

• NetBeans IDE .

Die Edition für“Java EE“¹¹ reagiert in beliebigen Projekten direkt auf die Annotationen der JPA – eine explizite Aktivierung der Unterstü ung ist nicht erforderlich.

• JetBrains IntelliJ IDEA (Ultimate Edition)

In der (kostenpflichtigen)”Ultimate Edition“¹² verfügt die Entwicklungsumgebung über Unterstü ung für die Java EE-Standards und hiermit auch JPA.

¹⁰ http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/neon2, aufgerufen am . Januar .

¹¹ https://netbeans.org/downloads/index.html, aufgerufen am . Januar .

¹² https://www.jetbrains.com/idea/download/, aufgerufen am . Januar .

(21)

Fehler Eclipse neon. NetBeans . IntelliJ IDEA

erkannt korrigierbar plausibel erkannt korrigierbar plausibel erkannt korrigierbar plausibel

7 7 7

3 7 3 3 7 3 3 (3)

7 7 3 7

3 7 7 3 7

3 7 3 3 7 3 3 (3)

7 7 3 7

7 7 7

Tabelle . .: Fehlererkennung und -behandlung in prominenten IDEs.

Zu jeder IDE zeigt Tabelle . an, ob der Fehler zur Entwicklungszeit erkannt und in der Be- nu erschni stelle markiert wurde (Spalte”erkannt“). Für erkannte Fehler stellt sich jeweils die Frage, ob Korrekturen angeboten werden (Spalte”korrigierbar“) und, falls ja, ob alle an- gebotene Korrekturen auch sinnvoll sind (Spalte”plausibel“);3 weist hier jeweils ein posi- tives Ergebnis aus,7ein negatives. Eclipse erkennt zwar drei Fehler, bietet jedoch keinerlei Korrekturen hierfür an. NetBeans erkennt ausschließlich die beiden fehlenden Primärschlüs- seldeklarationen (Fehler und ) und präsentiert sogar Vorschläge zur Korrektur. Diese sind jedoch nur mäßig hilfreich – so schlägt es etwa vor, das FeldDepartment.employeesmit@Id zu markieren, obwohl es keinen hierfür geeigneten Typ hat. IntelliJ IDEA erlaubt zwar aus- schließlich das Hinzufügen einesneuenFeldes, unterzieht den (als Freitext eingegebenen) Typ jedoch einer Plausibilitätsprüfung und weist eine ungültige Eingabe zurück. En äuschend ist jedoch die irreführende Beschreibung zu Fehler : Ansta das simultane Annotieren von Employee.departmentmit@OneToManyundDepartmentmit@Entityvorzuschlagen, wird die Nu ung des BehältertypenCollectionals unzulässig angemäkelt; dies ist fachlich so jedoch nicht korrekt (für Fehler gilt ähnliches).

Diese Bilanz ist en äuschend. Die Fehler und nicht zu erkennen, ist vielleicht verzeihlich, da die MethodeEntityManager.find(eEntity-Klasse,eSchlüssel)im Allgemeinen zwei beliebige Aus- drücke der Typenjava.lang.Class(eEntity-Klasse) undjava.lang.Object(eSchlüssel) akzeptiert und sich allenfalls durch eine aufwendige Kontrollflussanalyse ermi eln ließe, zu welchen Wer- teneEntity-Klasse undeSchlüsselauswerten. Gleichwohl liegt ein solch komplizierter Fall in Zeile nicht vor – sowohl das KlassenliteralDepartment.classals auch das Zeichenliteral”sales”

bezeichnen vom Kontrollfluss unabhängige Konstanten und erlauben auch bei einer rein sta- tischen Code-Analyse die Erkennung beider Fehler. Die anderen Defizite schönzureden, fällt hingegen noch schwerer – eine statische Analyse der Deklarationen hä e die Fehler hier pro- blemlos aufdecken können und sogar die Berechnung von Korrekturen erlaubt. All dies bleibt jedoch aus.

(22)

2.3. Ein Erklärungsansatz und eine Vision

Darüber, was die Hersteller der IDEs davon abhält, viel ausgereiftere Werkzeuge für die Nut- zung bestehender Frameworks (wie etwa JPA) berei ustellen, kann le tlich nur spekuliert werden. Selbst, wenn man die Betrachtung zunächst auf den Standard derJava Platform, En- terprise Edition (Java EE)beschränkt, erkennt man, dass diese”Landschaft“von einigen un- angenehmen Phänomenen geprägt ist:

• Eine bemerkenswerte Anzahl von Frameworks und sich im Detail unterscheidenden Implementierungen

Allein die Java EE-Spezifikation zählt in der Abbildung der Architektur [JEE , §EE. . ] Komponenten auf (in der Spezifikation wird vonAPIsgesprochen), die durch den Standard integriert werden. Neben der Java Persistence API fallen hierunter etwa Frame- works zur Behandlung von XML-Dateien, für entfernte Prozeduraufrufe (engl.Remote Procedure Calls, RPC) oder Transaktionsverwaltung.

Zudem existieren zu den Standards oft mehrere alternative Implementierungen ver- schiedener Hersteller. Für die JPA sind mit Hibernate ORM, EclipseLink¹³ und Open- JPA¹⁴ (vgl. Abschni . ) gleich drei alternative Lösungen verfügbar. Sie alle führen in- dividuelle Erweiterungen ein¹⁵ (für die auch ergänzende Wohlgeformtheitsregeln be- stehen) oder nehmen sich vom Standard abweichende Freiheiten heraus.¹⁶

• Eine oft fehlende oder unvollständige Spezifikation

Nicht allen Frameworks liegt eine exakte Spezifikation zugrunde – oft stehen neben der Implementierung nur einige Beispiele zur Verfügung, die Aufschluss über die bei der Nu ung geltenden Regeln geben können. Gerade bei Bibliotheken von Steuerelementen zur direkten Verwendung in Web-Oberflächen hat der Autor diese Erfahrung oft machen müssen.

• Sehr kurze Entwicklungs- und Release-Zyklen

Zu guter Le t unterliegen viele Frameworks sehr kurzen Entwicklungszyklen. Von Hi- bernate ORM etwa wurden allein im Jahr insgesamt Versionen veröffentlicht.¹⁷ Und nicht immer werden in neuen Versionen nur Fehler korrigiert. Der Autor hat hier von zusä lichen Plausibilitätsprüfungen bis zu erweiterten und reduzierten Schni stel- len alles schon einmal erlebt.

¹³ http://www.eclipse.org/eclipselink/

¹⁴ http://openjpa.apache.org

¹⁵ Die in Hibernate über den Standard hinausgehenden Funktionen werden nicht explizit aufgelistet. EclipseLink definiert in der Version . insgesamt gar Annotationen, die der JPA-Standard nicht kennt. Sie werden auf ei- ner dedizierten Internetseite (https://www.eclipse.org/eclipselink/documentation/2.6/jpa/extensions/toc.htm, auf- gerufen am . Januar ) diskutiert.

¹⁶ In Apache OpenJPA ist etwa die Deklaration eines Primärschlüssels optional; wird er nicht explizit dekla- riert, sieht der Provider implizit einen Surrogatschlüssel vor. Dieses Verfahren wirdDatastore Identitygenannt (vgl.http://openjpa.apache.org/builds/2.4.1/apache-openjpa/docs/ref_guide_pc_oid.html, aufgerufen am . Ja- nuar .

¹⁷ https://sourceforge.net/projects/hibernate/files/hibernate-orm/, aufgerufen am . Januar .

(23)

In dieser unübersichtlichen und schnelllebigen Umgebung kann von dem Hersteller einer IDE nicht realistisch erwartet werden, auch nur für einige wenige Frameworks aktuelle und ausgereifte Werkzeugunterstü ung vorzusehen. Eine

”Kennt-alles“-IDE muss daher Utopie bleiben. Gleichwohl hat die fehlende Berücksichtigung der Framework-spezifischen Regeln durch die IDE deutliche Nachteile für den Programmierer. Ansta sich auf die Implementie- rung der Anwendungslogik konzentrieren zu können, wartet er (mitunter minutenlang) auf eine Fehlermeldung, die eine statische Code-Analyse auch früher hä e aufdecken können.

Dieser Arbeit liegt die Annahme zugrunde, dass nur eine strikte Trennung zwischen der Implementierung der Entwicklungsumgebung, einerseits, und der Beschreibung der Frame- work- und Sprachspezifika, andererseits, einen Weg aus diesem Dilemma aufzeigen kann. Sie führt zu einer Arbeitsteilung zwischen den Framework- und IDE-Entwicklern, die es beiden Parteien erlaubt, sich auf ihre eigenen Kompetenzen zu konzentrieren:

• Den Entwicklern eines Frameworks kommt die Verantwortung zu, ihrem Produkt auch eine Spezifikation der geltenden Wohlgeformtheitsregeln mi uliefern. Diese sind unab- hängig von einer konkreten IDE und unabhängig von einem konkreten Anwendungs- zweck (etwa der Validierung oder der Korrektur).

• Die Entwickler einer IDE erhalten die Aufgabe, ihr Produkt mit einer Schni stelle aus- zusta en, über die diese Spezifikationen eingelesen werden können. Ebenso implemen- tieren sie Algorithmen, die das aktuelle Projekt im Hinblick auf diese Regeln validieren, bei Bedarf Korrekturen anbieten oder weiteres.

Für beide Seiten ergeben sich hierdurch ganz unmi elbar Vorteile: Framework-Entwickler können die Spezifikation der Wohlgeformtheitsregeln nu en, um das Programm im Rahmen des Bootstrappings zu validieren. Zudem haben jüngere Forschungsergebnisse gezeigt, wie solche Spezifikationen auch zur Generierung von Testfällen genu t werden können (vgl. Ab- schni . ). Eine konsequente Formulierung der Wohlgeformtheitsregeln kann sie so bereits bei ihrer eigenen Implementierungstätigkeit unterstü en.

Auf der anderen Seite brauchen die Entwickler einer IDE nicht länger hinter den Weiterent- wicklungen verschiedener Frameworks herzulaufen. Sta dessen können sie sich darauf kon- zentrieren, die Umgebung benu erfreundlicher zu gestalten oder die allgemeinen, Frame- work-unabhängigen Algorithmen zu verbessern.

Bis dahin ist es jedoch ein weiter Weg!

Die vorliegende Arbeit wagt einige erste Schri e in diese Richtung. Sie se t auf den Arbei- ten zuConstraint-basierten Entwicklungswerkzeugenauf, die eine Programmieraufgabe als Such- problem beschreiben und durch wohlbekannte Algorithmen lösen. Zur deklarativen Formu- lierung der Wohlgeformtheitsregeln kommt hierbei dieObject Constraint Language (OCL)zum Einsa . Die Grundlagen und die sich aus der vorgestellten Vision ergebenden Forschungs- ziele werden in den folgenden Abschni en präsentiert.

2.4. Constraint-basierte Entwicklungswerkzeuge

Ein Constraint-basiertes Entwicklungswerkzeug modelliert ein Problem der Softwareprogram- mierung als Constraint Satisfaction Problem (CSP) [SKP ; Ste ; Tip+ ]. Hierbei handelt

(24)

es sich um eine Beschreibung eines Suchproblems aus dem Bereich der künstlichen Intelli- genz, für das über die Zeit viele effiziente Lösungsverfahren vorgeschlagen wurden [Tsa ; RN ]. Die Grundlagen von CSPs und ihre Verwendung zur Behandlung eines Programmier- problems sollen in den folgenden Abschni en kurz erklärt werden; ein ausführlicherer Über- blick über den Stand der Forschung wird in Abschni . nachgereicht.

Constraint Satisfaction Problems EinConstraint Satisfaction Problem (CSP)⟨V, D, C⟩besteht aus einer Menge von Variablen V = {v1, ..., vn} mit ihren Domänen D = {d1, ..., dn}¹⁸ und einer Menge von ConstraintsC ={c1, ..., cm}. Ein Constraintci ist ein boolescher Ausdruck, in dem die Variablen ausV auftreten können.

DasLöseneines CSP besteht in der Suche einer WertezuweisungS ={v1 ←w1, ..., vn←wn} für alle Variablen vi mitwi di, die alle Constraints ausC erfüllt.S wirdLösung des CSP

⟨V, D, C⟩genannt. Die Menge aller Lösungen{S1, ..., Sl}heißtLösungsmengeund wird mitS bezeichnet. Existiert keine Lösung (S = ∅), so sind die Constraints nicht miteinander oder nicht mit den Domänen der Variablen vereinbar und das CSP heißtinkonsistent.

CSPs stellen einen wohlbekannten Formalismus zur Beschreibung von Suchproblemen dar.

Über die Zeit haben sich verschiedene (zumeist in Kombination verwendete) Methoden zur effizienten Lösungssuche etabliert, etwa Constraint Propagation, Forward Checking und Back- tracking Search[RN ]. Ebenso existieren heute verschiedeneConstraint-Solver, die diese im- plementieren, um die Lösungsmenge zu einem gegebenen CSP zu bestimmen. Der Constraint- SolverChoco¹⁹ ist hierfür ein Beispiel; er kommt in den Implementierungen zur vorliegenden Arbeit zum Einsa .

Grundidee Die Darstellung einer Programmieraufgabe als CSP⟨V, D, C⟩ erlaubt es, diese Suchverfahren und Constraint-Solver zu nu en, um die Aufgabe zu lösen. Eine Transforma- tion eines Programms kann bestimmt werden, indem verschiedene Änderungen an dem Pro- gramm zugelassen werden, die dem Constraint-Solver einen Entscheidungsspielraum bei der Suche einer Lösung einräumen. Die Herausforderung bei der Implementierung eines Con- straint-basierten Entwicklungswerkzeugs besteht darin, das CSP so zu definieren, dass nur solche Transformationen Lösungen des CSP sind, die eine bestimmte, gewünschte Eigenschaft vorweisen. Der Programmierer des Werkzeugs kann sich so auf eine deklarative, exakte Spe- zifikation desWas konzentrieren, ansta sich über eine imperative (häufig lückenhafte und fehleranfällige) Beschreibung desWieden Kopf zerbrechen zu müssen.

Konkret sind hierzu drei Fragen zu beantworten:

. Wie kann das Programm (oder zumindest ein klar umrissener Aspekt von ihm) als eine Menge von Constraint-Variablen dargestellt werden?

. Welche Eigenschaft sollen die berechneten Programmtransformationen vorweisen und wie kann sie durch Constraints formuliert werden?

¹⁸ Genauer ist die Darstellung der DomänenD in⟨V, D, C⟩als FunktionD : V D, die jedesv V auf eine eindeutige DomänedDabbildet. Hierbei giltD(vi) =di[Tsa , § . . ]. Häufig wird diese Zuordnung jedoch lediglich informell beschrieben oder als implizit gegeben betrachtet und diedieinfach aufgezählt [RN ,

§ . ] [Van , § . . ]. Die vorliegende Arbeit folgt dieser Vereinfachung.

¹⁹ http://www.choco-solver.org

(25)

FieldDeclaration

jpaIsId : Boolean

ClassDeclaration

jpaIsEntity : Boolean

fields type

Abbildung . .: Ein einfaches Metamodell für JPA-Programme.

. Welche Änderungen sind an dem Programm zugelassen und wie müssen hierzu die Domänen definiert werden?

Diese drei Fragen werden im Folgenden kurz beleuchtet. Zur Illustration dient das Pro- gramm kurz vor der Behandlung von Fehler . Der KlasseEmployee wurde hier gerade die

@Entity-Annotation angefügt, jedoch fehlt die Deklaration eines Primärschlüssels. Um das Bei- spiel kompakt zu halten, ignoriert es die KlasseDepartment(mitsamt der enthaltenen Felder).

Abbildung eines Programms auf Constraint-Variablen Für die Aspekte des Programms, die Gegenstand des Programmierwerkzeugs sind, muss zunächst eine Darstellung durch Con- straint-Variablen gefunden werden. Das Programm wird hierzu als Instanz einesMetamodells aufgefasst, durch das die Konzepte der zugrundeliegenden Programmiersprache (oder ein Ausschni hiervon) beschrieben werden.

Abbildung . zeigt ein stark reduziertes Metamodell für JPA-Programme, das aus zweiMe- taklassen,ClassDeclarationundFieldDeclaration, besteht. Die Metaklasse ClassDeclarationver- fügt über zweiA ribute²⁰: Ob die durch eine Instanz repräsentierte Klasse im Quellcode mit der @Entity-Annotation versehen ist, zeigt das boolesche A ribut jpaIsEntityan. Über fields wird die Menge vonFieldDeclaration-Instanzen referenziert, die für die in ihr deklarierten Fel- der stehen.FieldDeclarationverfügt über das A ributjpaIsId, das kennzeichnet, ob dieser Feld- deklaration die@Id-Annotation angefügt wurde. Zudem referenzierttype diejenige Instanz vonClassDeclaration, die ihren deklarierten Typ repräsentiert.

Die Instanz des Metamodells, die ein konkretes Programm repräsentiert, wirdProgramm- modellgenannt. Die in ihm definierten Objekte repräsentieren jeweils einen kleinen Teil des Quellcodes; sie heißenProgrammelemente(oder, wenn der Zusammenhang klar ist, kürzerEle- mente) und sind Instanzen der Metaklassen. In dem Programmmodell zum Code aus Abbil- dung . wird die KlasseEmployeeetwa durch ein Programmelement repräsentiert, das eine Instanz vonClassDeclarationist und mitCEmplbezeichnet wird.

Bei der Instanziierung eines Programmelements aus einer Metaklasse wird für jedes in ihr definierte A ribut ein eigenes Merkmal vorgesehen. CEmpl verfügt etwa über das Merk- malCEmpl.jpaIsEntity, welches angibt, ob der KlasseEmployeedie@Entity-Annotation angefügt wurde;CEmpl.fieldsreferenziert die ProgrammelementeFEmpl.id undFEmpl.dept. Zu den beiden le tgenannten gehört analog jeweils einjpaIsId- und ein type-Merkmal. Tabelle . führt sie in der linken Spalte auf. Die zweite Spalte der Tabelle gibt dieaktuelle Wertebelegungan; diese besteht aus den Werten, die den Zustand des unveränderten Programms beschreiben. Die in

²⁰ Auf eine sprachliche Differenzierung zwischen (mit primitiven Typen wieinttypisierten)A ributenund (mit Klassen- oder Schni stellendeklarationen typisierten)Referenzenwird der Einfachheit halber verzichtet – sie alle werden mitA ributbezeichnet.

(26)

Merkmal aktuelle Wertebelegung Wertebereich (zur Validierung) (zur Korrektur)

CEmpl.jpaIsEntity true {true,false}

CEmpl.fields [FEmpl.id,FEmpl.name] [FEmpl.id,FEmpl.name]

FEmpl.id.jpaIsId false {true,false}

FEmpl.id.type CLong CLong

FEmpl.dept.jpaIsId false {true,false}

FEmpl.dept.type CDept CDept

Tabelle . .: Merkmale und ihre (möglichen) Werte bei Validierung und Korrektur.

eckigen Klammern dargestellte Aufzählung der Werte vonCEmpl.fieldsstellt hier eine Menge als Wert eines mehrwertigen Merkmals dar.²¹

Für jedes Merkmal wird bei der Erzeugung eines CSP nun eine eigene Constraint-Varia- ble vorgesehen. Zwar besteht aus technischer Perspektive zwischen ihnen ein Unterschied²², konzeptionell sind sie jedoch äquivalent.

Spezifikation der Constraints Die ConstraintmengeC des CSP muss so definiert werden, dass sie die gewünschte Eigenschaft des Programms beschreibt. Im Kontext des diskutierten Beispiels ist dies dieWohlgeformtheit der JPA-Metadaten eines Java-Programms. Die Constraints sollen daher so entworfen werden, dass

(a) jede aktuelle Belegung eines wohlgeformten JPA-Programms alle Constraints erfüllt und (b) jede Wer uweisung, die alle Constraints erfüllt, eine wohlgeformte Variante des Pro-

grammcodes darstellt.

Kriterium (a) ist Vorausse ung dafür, dass bei einer Prüfung der Korrektheit eines JPA-Pro- gramms keine Programme fälschlicherweise als ungültig erkannt werden; Kriterium (b) ist Bedingung dafür, dass bei der Suche einer (ein fehlerhaftes) Programm reparierenden Trans- formation keine Folgefehler injiziert werden.

Ansta bei jedem Programm erneut über die notwendigen Constraints nachzudenken, wer- den die Wohlgeformtheitsregeln eines Frameworks (oder, allgemeiner, einer Sprache) unab- hängig von dem konkreten Einzelfall formuliert. DieInvariantenbeziehen sich sta dessen auf die Klassen des Metamodells und beschreiben die Regeln in generischer Weise.²³ Eine Invari-

²¹ Die rechte Spalte wird unten näher erläutert.

²² Bei den Constraint-Variablen sind die Einschränkungen des verwendeten Constraint-Solvers zu berücksichti- gen. Choco kennt etwa lediglich Variablen vom Typ einer Zahl, eines Wahrheitswertes oder einer Menge. Um zu einem Merkmal, das einen benu erdefinierten Typen zum Wert hat (etwaFEmpl.id.type, das eine Instanz von ClassDeclarationreferenziert), eine Constraint-Variable zu initialisieren, müssen die möglichen Werte dieses Typen auf Werte eines dem Solver bekannten Typen abgebildet werden. Im Beispiel könnte etwa für alle In- stanzen vonClassDeclarationein eindeutiger, ganzzahliger Identifizierer vorgesehen werden. Zu einem Merk- mal wieFEmpl.id.typekann somit eine ganzzahlige Constraint-Variable erzeugt werden, deren Domäne sich aus der Menge der Identifizierer des Typen ergibt.

²³ In Vorarbeiten wird sta von Invarianten vonConstraint-Regelngesprochen. Ihre Darstellung unterscheidet sich von der hier gewählten und wird in Abschni . näher diskutiert.

(27)

anteInvsoll vorerst wie folgt notiert werden:

Inv:∀v∈JMetaklasseK:b(v)

JMetaklasseKsteht hierin für die Menge aller Instanzen der MetaklasseMetaklasse. Die Erset- zung der (freien) Variablevinb(v)durch einen WertEdieser Menge wirdInstanz vonInvfür E genannt. Tri inb(v)ein quantifizierter Ausdruck auf, so wird dieser im Rahmen der In- stanziierung durch einen äquivalenten logischen Ausdruck erse t,²⁴ was im Folgenden noch illustriert wird.

Die folgenden drei Invarianten beschreiben die (vereinfachten²⁵) Bedingungen, die eine mit

@Entityannotierte Klasse zur gültigen Deklaration eines Primärschlüssels erfüllen muss:

• Jede Klasse eines JPA-Programms, die über die@Entity-Annotation verfügt, enthält ein Feld, welches mit@Idannotiert ist.

EntityHasId:∀c∈JClassDeclarationK:c.jpaIsEntity→ ∃f ∈c.fields:c.jpaIsId

• In jeder Klasse darf höchstens ein@Id-Feld deklariert werden.

OnlyOneId:∀c∈JClassDeclarationK:∀f1, f2∈c.fields, f1 ̸=f2 :f1.jpaIsId→ ¬f2.jpaIsId

• Ein@Id-Feld muss entweder mitjava.lang.Stringoder mitjava.lang.Longtypisiert sein.

ValidIdType:∀f JFieldDeclarationK:f.jpaIsId→f.type∈ {CLong,CString}

Durch Anwendung der Invarianten auf das Programmmodell wird jede Invariante für die betreffenden Programmelemente instanziiert. Für den betrachteten Ausschni (also die Ele- menteCEmpl,FEmpl.idundFEmpl.dept) entstehen insgesamt vier Constraints:

• Bei der Instanziierung vonEntityHasIdfürCEmplwird der Existenzquantor∃f ∈c.fields: c.jpaIsIdunter Berücksichtigung des aktuellen Wertes vonc.fieldsdurch eine Disjunktion erse t:

CEmpl.jpaIsEntity→FEmpl.id.jpaIsId∨FEmpl.dept.jpaIsId

• Analog wird der Allquantor inOnlyOneIddurch eine Konjunktion erse t:

(FEmpl.id.jpaIsId→ ¬FEmpl.dept.jpaIsId)∧(FEmpl.dept.jpaIsId→ ¬FEmpl.id.jpaIsId)

• Zule t wirdValidIdTypefür die beiden Felder instanziiert:

FEmpl.id.jpaIsId→FEmpl.id.type∈ {CLong,CString} FEmpl.dept.jpaIsId→FEmpl.dept.type∈ {CLong,CString}

Eine vollständige Beschreibung der Wohlgeformtheitsbedingungen erfordert (selbst in dem kleinen Beispiel) die ergänzende Spezifikation von Invarianten für@OneToMany- und@Many- ToOne-Assoziationen sowie die Instanziierung von Constraints für die verbleibenden Elemen- te des Programmmodells (das heißtCDeptund die enthaltenen Felder). Der Übersichtlichkeit halber soll dies jedoch in diesem Beispiel ausbleiben. Wie unten gezeigt wird, ist für diesen Kompromiss jedoch bei der Korrektur des Programms gleich ein Preis zu zahlen...

²⁴ Im Wesentlichen wird ein allquantifizierter Ausdruckx∈ {X1...Xn}:b(x)durch eine Konjunktionb(X1) ...b(Xn)erse t und ein existenzquantifizierter analog durch eine Disjunktion. Die Details dieses Vorgehens werden in Abschni . . beleuchtet.

²⁵ Ein Sa von exakten Invarianten zur Definition des Primärschlüssels wird in Abschni . . vorgestellt.

Referenzen

ÄHNLICHE DOKUMENTE

In diesem Kapitel werden zunächst einige für diese Arbeit grundlegende Begriffe eingeführt (2.1), darauf folgend werden Stationarität und Instationarität in Zeitreihen

Diese Überlappung der Lauteigenschaften ist für den Hörer kein Problem und für den Sprecher vorteilhaft: “High-speed communication with slow-speed machinery”... Beispiel:

Unified modeling language: Infrastructure, version 2.4.1. Unified modeling language: Superstructure,

In ihrer Darstellung der Verbreitung und Variabilität von Phelsuma mada- gascariensis MERTEN S, 1954 weisen MEIER &amp; Bö HME (1991) darauf hin, daß die Kenntnis der

This work therefore describes an approach to integrate fuzzy sets and constraints expressed in the Object Constraint Language (OCL) in a combined constraint reasoning process..

Wenn diese Vorgehensweise auf eine Menge gleichartiger Systeme angewandt wird, k¨onnen Ans¨atze aus der modellbasierten Entwicklung und dem Produktlinien-Engineering kombiniert

Ansätze zu Produktlinien und Produktfamilien beschäftigen sich meist mit den Anforderungen und der Architektur, die Erweiterbarkeit und Variabilität ausdrückt und modelliert?.

Um die Verwechselungsgefahr zwischen Software- und Produktlinienvariabilität zu minimieren und aufgrund der oben genannten Nachteile der integrierten Dokumentation von