Farbe! Generative Software Engineering «interface» Interface Class Type attribute 1 role CD Class * 2. Generating Data Explorers composition Type method() qualifier qualified_association Prof. Dr. Bernhard Rumpe http://www.se-rwth.de/ Page 2 Data Explorer (DEX) DEX is a generator for (parts of) business applications Input: Class Diagram, (OCL constraints) Generated result Running application UML/P CD DEX generator textual class diagram Java Swing Application We use DEX to study how a generator works how to adapt and extend it s components
Farbe! Generative Software Engineering «interface» Interface Class Type attribute 1 role CD 2. Generating Data Explorers 2.1. Class Diagrams as Input Class composition Type method() qualifier qualified_association * Prof. Dr. Bernhard Rumpe http://www.se-rwth.de/ Page 4 Class Diagrams We assume that you are familiar with CDs, here a short overview (and excerpt of our case study Social Network ): this is a class diagram Relationship boolean pending Date requested Date accepted 1 «enum» RelationType FRIEND FAMILY COLLEAGUE FOLLOWER OTHER * invited 1 * initiated 1 Person Date lastvisit String firstname String secondname Date dateofbirth «abstract» Profile String profilename /int numberofposts /int friends * * member Group boolean isopen Date created String purpose /int headcount CD organizer 1 profilename * organized
Page 5 Social Network The Full Domain Model Relationship boolean pending Date requested Date accepted «enum» RelationType FRIEND FAMILY COLLEAGUE FOLLOWER OTHER * invited 1 * initiated 1 Date lastvisit String firstname String secondname Date dateofbirth int zip String city String country tagged 1 «abstract» Profile String profilename /int numberofposts /int friends 1 * InstantMessage 0..1 Person * * Group replyto Date timestamp member String content organizer 1 * 1 sent * {ordered * received * {ordered boolean isopen Date created String purpose /int members profilename Tag organized boolean confirmed * * 1 «interface» Post PhotoMessage picture 1.. * Photo double height double width Page 6 Concepts we know in CD s Classes Interfaces, abstract classes, enumerations Attributes Name + Type + Visibility derived attributes Associations Name, role names, cardinalities Composition Qualified associations Qualifier {ordered association Stereotypes like <<singleton>>
Page 7 We use a textual notation for CDs Text is easier to process Text is easier to define A class diagram thus becomes a text-file of this form and is stored in SocNet.cd classdiagram SocNet { CD class... class... association... We next define the textual input Page 8 Textual Form for Classes A class looks pretty similar to Java code, but no methods: classdiagram SocNet { «interface» Post interface Post; class InstantMessage implements Post { Date timestamp; String content; InstantMessage Date timestamp String content
Page 9 Textual Form for Enumerations An enumeration in a CD looks like this (and can be used as type elsewhere in the class diagram): Relationship class Relationship { boolean pending; Date requested; Date accepted; enum RelationType { FRIEND, FAMILY, FOLLOWER, COLLEAGUE, OTHER; boolean pending Date requested Date accepted 1 «enum» RelationType FRIEND FAMILY COLLEAGUE FOLLOWER OTHER association Relationship -> RelationType [1]; Page 10 Textual Form for Associations -1 An association in the CD: Person * * member Group association member [*] Person <-> Group [*] assoc. name navigation direction, class name multiplicity, e.g. [*], [1], [2..6], [0..1] Navigation directions: ->, <-, <-> and Assoc. name, multiplicities are optional
Page 11 Textual Form for Associations -2 An association in the CD: Person Group * organizer profilename * organized association [*] Person (organizer) <-> (organized) [profilename] Group [*]; role name qualifier Role names for navigation Qualifier is an attribute of the opposite class Page 12 Lets define classes: «abstract» Profile abstract class Profile { String profilename; /int numberofposts; /int friends; class Group extends Profile { boolean isopen; Date created; String purpose; /int headcount; String profilename /int numberofposts /int friends Group boolean isopen Date created String purpose /int headcount
Page 13 Lets define more: «interface» Post interface Post; class InstantMessage implements Post { Date timestamp; String content; 0..1 InstantMessage replyto Date timestamp String content * PhotoMessage association [*] InstantMessage <-> (replyto) InstantMessage [0..1]; class PhotoMessage extends InstantMessage; Page 14 Grammar of UML/P CD lite language Appendix CDDefinition = "classdiagram" Name "{ (CDClass CDInterface CDEnum CDAssociation)* "" CDClass = "abstract"? "class" Name ("extends" ReferenceType ("," ReferenceType)*)? ("implements" ReferenceType ("," ReferenceType)*)? (CDClassBody ";") CDClassBody = "{" CDAttribute* "" CDAttribute = "/"? Type Name ("=" Value)? ";" CDInterface = MG "interface" Name ("extends" ReferenceType ("," ReferenceType)*)? ";" CDEnum = "enum" Name (CDEnumBody* ";") CDEnumBody = "{" (CDEnumConstant ("," CDEnumConstant)* ";")? "" CDEnumConstant = Name CDQualifier = "[" Name "]" RoleName = "(" Name ")" CDAssociation = "association" "/"? Name? Cardinality? QualifiedName CDQualifier? RoleName? ( "->" "<-" "<->" "--" ) RoleName? CDQualifier? QualifiedName Cardinality? "<<ordered>>"? ";" Cardinality = "[" ("*" Int Int ".." Int Int ".." "*") "]"; This is the compact, readable grammar for the concrete syntax. For the full language spec please see: http://www.monticore.de/dex
Page 15 The full in textual form -1 Appendix package dex; import java.util.*; classdiagram SocNet { abstract class Profile { String profilename; /int numberofposts; /int friends; class Person extends Profile { Date lastvisit; String firstname; String secondname; Date dateofbirth; int zip; String city; String country; class Group extends Profile { boolean isopen; Date created; String purpose; /int headcount; association member [*] Person <-> Group [*]; association [1] Person (organizer) <-> (organized) [profilename] Group [*]; Excerpt from SocNet.cd Page 16 The full in textual form -2 Appendix class Relationship { boolean pending; Date requested; Date accepted; association invited [*] Relationship <-> Profile [1]; association initiated [*] Relationship <-> Profile [1]; enum RelationType { FRIEND, FAMILY, FOLLOWER, COLLEAGUE, OTHER; association Relationship -> RelationType [1]; interface Post; association received [*] Profile <-> Post [*] <<ordered>>; association sent [1] Profile <-> Post [*] <<ordered>>; class InstantMessage implements Post { Date timestamp; String content; association [*] InstantMessage <-> (replyto) InstantMessage [0..1]; class PhotoMessage extends InstantMessage; association [1..*] Photo (picture) <-> PhotoMessage; class Photo { double height; double width; class Tag { boolean confirmed; association [1] Person (tagged) <-> Tag [*]; association [*] Tag <-> Photo [1];
Farbe! Generative Software Engineering 2. Generating Data Explorers 2.2. How to Use DEX Prof. Dr. Bernhard Rumpe http://www.se-rwth.de/ Page 18 Data Explorer (DEX) DEX is a generator for (parts of) business applications Input: Class Diagram, OCL constraints Generated result Running application for managing data fitting to the class diagram A graphical user interface Support for storing data in the cloud UML/P CD DEX generator The model: A textual class diagram DEX product: Java Swing Application
Page 19 DEX Product Features Frontend features CRUD (create, read, update, delete) management for objects and associations Browsing, search and filter functionality Support for Java types (e.g. List<int>, Date) Feedback on invalid input Uses dummy values for testing Backend features Database support Multiuser support Page 20 Using the DEX generator Important steps for installation 1. Install JDK Version 6 or higher 2. Set JAVA_HOME path variable to the JDK directory e.g. to C:\Programme\Java\jdk1.7.0_51 3. Download dex-cli-*-snapshot.zip from http://www.monticore.de/dex/ 4. Unzip it 5. Go to dex-cli 6. Run the DEX generator java -jar dex-cli-*-snapshot.jar examples/dex/socnet.cd out 7. Launch generated result: Go to out java -cp "./gen/;./gen/rte.jar" dex.socnet.socnet Sources, installation, user instructions, trouble shooting http://www.monticore.de/dex/ Parameters for the generator are (can be adapted): examples/dex/socnet.cd source CD out target directory for all code
Page 21 Lets do a DEX generator demonstration Let s modify our SocNet-example adapt the SocNet.cd file and lets make another example create or use other cd-files classdiagram MyOwnCD { class... DEX generator Farbe! Generative Software Engineering 2. Generating Data Explorers 2.3. Architecture of the Generated Product Prof. Dr. Bernhard Rumpe http://www.se-rwth.de/
Page 23 Structure of a generated Product This is the principal structure of the generated code: Now we have a look, how DEX builds sub-structures model Parameterized generator manually written code API generated code + included parts API Predefined Predefined Predefined components runtime system Generator script/template Environment: hardware, GUI, frameworks Map: concept code Page 24 Structure of Generated Files The generator stores all files into dex-cli/out /symtab symboltable entries (not so interesting for the moment) /gen/protocol, /gen/reporting detailed information about how dex generates files (cryptic) /gen/dex the desired java classes Java has its own package structure reflected in directories, so actually we have all classes generated from SocNet.cd in /gen/dex/socnet /gen/dex/socnet/gui /gen/dex/socnet/gui/impl /gen/dex/socnet/persistence In total we have 206 Java files (from 10 classes in SocNet) (on April 11, 2014)
Page 25 Architectural Pattern: 3-Tier Reference Architecture DEX structures its generated products in the same way as standard applications User interface never directly accesses (imports) persistence Persistence encapsulates access to data storage (includes DB) Application core is derived from domain data model Dimension #D1 of product structure: An appropriate architectural pattern / style The architectural pattern: SocNet (generated) package structure: GUI storage commands socnet/gui socnet/gui/impl Application Core socnet data structure Persistence socnet/persistence Page 26 Examining Generated Files for SocNet.cd Counting classes and loc: Package #classes #loc socnet 51 10967 socnet/gui 26 2368 socnet/gui/impl 106 24366 socnet/persistence 23 4136 Sum: 206 41837 SocNet: 10 classes, 11 associations, ~ 70 #loc Result: a lot can be generated. But how much really needs to be generated?
Page 27 How individual are Generated Files? -1 Experiment: take Experiment.cd with only one class XSpecific, no attributes Result Package #classes #loc... 8 684.../gui 3 80.../gui/impl 12 1034.../persistence 7 379 Sum: 30 2177 2177 loc in total, but only 927 unique lines. XSpecific occurs 321 times in many variants (incl. comments) 19 Java filenames contain XSpecific Page 28 How individual are Generated Files? -2 Experiment 2: we compare SocNet.cd and Experiment.cd Package #classes Experiment SocNet... 8 51.../gui 3 26.../gui/impl 12 106.../persistence 7 23 Sum: 30 206 Result #loc #loc SocNet 8512 unique (41837 total) Experiment 927 unique ( 2177 total) of which 630 lines are shared with SocNet and 297 individual to Experiment One filename in common (but different content)
Page 29 How individual are Generated Files? -3 Experiment 3: compare two sets of generated files from same source SocNet.cd Result #loc #loc SocNet 8512 unique (41837 total) of which 8507 lines are equal and 5 lines are individual Reasons for changed lines: Date and timestamp of generation process, e.g. "10:20:39 on day 11.04.2014" // generated at 10:20:39 on day 11.04.2014 from Page 30 How much Code is Generated? Some experiments show An empty CD with zero classes has ~ 450 #loc For each additional element we get roughly ~ 1800 #loc / class ~ 150 #loc / attribute ~ 1600 #loc / *-* association, bidirectional ~ 900 #loc / 1-1 association, unidirectional Results vary, because of different forms of attributes and classes
Page 31 Common Code - Runtime Environment (RTE) DEX products also consist of immutable classes always in the product, independent of the domain model see: out/gen/rte.jar with currently more than 3425 classes DEX uses e.g. java.util.* etc. standard functionality (e.g. Lists) com.google.common.* useful stuff com.jidesoft.awt.* for the GUI dex.* DEX RTE mc.*, mc.umlp.cd.* MontiCore runtime and more DEX uses a DEX specific Runtime Environment (RTE) The RTE has ~195 classes, in the dex.* packages Dimension #D2 of product structure: Generated Code vs. RTE vs. Standard Components (to be extended later by handcoding) Page 32 Runtime Environment (RTE) Runtime Environment (RTE) implements the core behavior of a computer language. (Wikipedia) is a predefined set of classes assisting the generated code to fulfill its function. (own) RTE is usually defined as common, domain independent functionality used by the generated code or imported from existing libraries / frameworks Synonyms: run-time system, runtime system, or just runtime
Page 33 Generated Code vs. RTE vs. Standard Components According two the two dimensions #D1, #D2 the DEX product architecture thus looks like this: GUI generated RTE standard components Application Core generated RTE standard components Persistence generated RTE standard components Legend: reflect possible static knowledge (import) Specific classes may import more general classes Farbe! Generative Software Engineering 2. Generating Data Explorers 2.4. Mapping Domain Classes to Code Prof. Dr. Bernhard Rumpe http://www.se-rwth.de/
Page 35 Mapping of the Concepts of CD Lite The DEX generator maps each concept of CD Lite to code Classes Interfaces, abstract classes, enumerations Attributes Name + Type derived attributes Associations Name, role names, cardinalities Qualified associations Qualifier {ordered association So we need to understand how these concepts (and their variations) are mapped to code We concentrate on the application core of the product, because we will later be interested to use and extend this core Page 36 Mapping a Domain Class to what? Given class Person: What should we generate in the application core? class Person extends Profile { String firstname; // interface Person class PersonImpl factory PersonFactory for object creation manager for existing Person Instances
Page 37 Mapping a Domain Class to an Interface Given class Person: DEX generates an interface signature of the domain objects attributes get access functions class Person extends Profile { String firstname; // some standard functionality necessary for each domain object public interface Person Java extends dex.data.dexobject, dex.socnet.profile { public String getfirstname(); public void setfirstname(string firstname); // methods for each attribute Page 38 Mapping a Domain Class to an Implementation Each class gets a default implementation DEX generates 2 constructors, getter/setter class Person extends Profile { String firstname; // public class PersonImpl extends dex.socnet.profileimpl Java implements dex.socnet.person, dex.socnet.profile { protected PersonImpl() { super(); protected PersonImpl (String firstname, ) { this.firstname = firstname; // public String firstname ; public String getfirstname() { return firstname; public void setfirstname(string firstname) { this.firstname = firstname; //
Page 39 Additional Implementation elements DEX generates some additional implementations for methods from Object dex.data.dexobject class Person extends Profile { String firstname; // public class PersonImpl extends dex.socnet.profileimpl Java implements dex.socnet.person, dex.socnet.profile { // their behavior is based on all domain attributes public int compareto(dex.socnet.profile o) { public int hashcode(){ // these are used for deletion of objects public void removeobjfromassocs() { // and for representation in the GUI public String dexguigetscreenname() { Page 40 Mapping Model Inheritance to Code Inheritance and implementation relation is preserved (CD lite doesn t allow multiple inheritance, nor does DEX need it) Model-CD interface A; interface B; class C implements A; class D implements B inherits C; «interface» A «interface» C CImpl Product-CD «gen» «interface» B «interface» D DImpl
Page 41 Mapping a Derived Attribute A derived attribute generates getter signature no attribute, no setter empty getter implementation (the class is complete and can be instantiated) class Group extends Profile { String /headcount; // The signature is given, but the implementation is useless and needs to be added by handcoding (see Chapter 3) or by generating the implementation from OCL (see later) public class GroupImpl extends { public int getheadcount() { return 0; // nothing to support calculation or storage of headcount Java Page 42 Object Creation for a Domain Class Normally to create an object : constructor is called, thus class must be known (instead of interface) Adaptation by subclasses is then not possible. Solution: Use factory instead of constructor. DEX generates: class Person extends Profile { String firstname; // public class PersonFactory { Java // // public access through static method (Singleton) // empty default constructor public static Person create() { // finally it executes: return new PersonImpl(); // 2nd constructor with attributes as parameters (in order they appear) public static Person create(string firstname, ) {
Page 43 Object Management for a Domain Class Given class Person DEX generates: a PersonManager it serves as access point to load/store objects manages the collection of objects of a type (incl. subclasses) manager uses different implementations (online / demo mode) class Person extends Profile { String firstname; // public class PersonManager { Java // public static void remove(collection<person> objs) { public static void add(person obj) { public static void update(person obj) { public static void synchronize() { public static Person get(person obj) { public static void add(person obj) { public static boolean contains(person obj) { public static void clear() { Page 44 Views of a Domain Class A class in the domain model serves several viewpoints: 1) How to assess a class (for the generator user) the interface Person and the factory interface PersonFactory 2) How does it work internally (for the generator writer) the implementation PersonImpl 3) How can I adapt and extend the generated code and interface (for the product programmer) later (see Chapter 3) Decoupling into interface and implementation nicely corresponds to the first two viewpoints
Farbe! Generative Software Engineering 2. Generating Data Explorers 2.5. Mapping Associations to Code Prof. Dr. Bernhard Rumpe http://www.se-rwth.de/ Page 46 Internals of SocNet Core - Associations Associations in CDs are realized in DEx with respect to their Cardinality e.g. 0,1,* Direction ->, --, <->, <- Modifiers abstract, / (derived) Qualifiers e.g. [qualifier], <<ordered>> Optional role and association names The core idea is to map the variants of associations to appropriate signatures and implementations The developer only needs to deal with the signatures But we also look at one or the other implementation to understand the generator and possibly adapt it.
Page 47 Unidirectional Association, Cardinality [1]: Unidirectional association with cardinality [1] is mapped to methods for set/get (quite like an attribute) Additional, internal signatures are created e.g. when the object is deleted, so shall be the link. association Relationship -> RelationType [1]; interface Relationship { public RelationType getrelationtype(); public void setrelationtype(relationtype o); Java Direction determines where the methods are located methods are added on the source side of each navigation Page 48 Bidirectional Associations, Role Names Bidirectional association provide methods for link management on both sides here is the direction <- and the role name given: association [1] Person (tagged) <-> Tag [*]; interface Tag { public Person gettagged(); public void settagged(person o); Java + methods for class Person for other direction
Page 49 Association, Cardinality [*]: Association with cardinality [*] is mapped to methods for collection management (quite like the Set<X> signature) association [1] Person (tagged) <-> Tag [*]; interface Person { public boolean containsalltags(collection<tag> o); public boolean containstag(tag o); public boolean isemptytags(); public Iterator<Tag> iteratortags(); public int sizetags(); public boolean addtag(tag o); public void cleartags(); public boolean removetag(tag o); Java Page 50 Implementation of Associations Implementation depends on cardinality: [1], [0..1] map to an ordinary attribute [*], [1..*], [2..7] etc. map to a collection association [1] Person (tagged) <-> Tag [*]; public class PersonImpl implements Person { private java.util.set<tag> tags = new java.util.hashset<tag>(); // Tag-methods delegate functionality to Set, e.g. public boolean containstag(tag o) { return tags.contains(o); public class TagImpl implements Tag { private Person tagged; // + getter/setter Redundant storage of links on both sides! Consistency needs to be ensured Java Java
Page 51 Consistency in Bidirectional Associations We need to ensure that links are consistent on both sides adding a link automatically adds the link on the other object association [1] Person (tagged) <-> Tag [*]; DEX generates additionaly xgenxaddtag, xgenxremovetag xgenx-methods are assessed from outside: thus public, but should not be used by the developer: thus unusual name public class PersonImpl { public boolean xgenxaddtag(tag o) { return tags.add(o); public boolean xgenxremovetag(tag o) { return tags.remove(o); public boolean addtag(tag o) { o.xgenxsettagged(this); return tags.add(o); Java Called from opposite side of link to make Bidirectional consistent Calling the opposite link Page 52 Unspecified Cardinality on Associations In case a cardinality isn t given, defaults are taken: navigable side is assumed to be [0..1] not navigable side is assumed to be [*] Thus: A -> B [*] A -> B [0..1], A <-> B [0..1] A <-> B [0..1], A -- B [0..1] A -- B [0..1] default:bidirectional Please note: not navigable direction still store references in opposite directions for consistency reasons, e.g. in case when target objects are deleted (exception enums)
Page 53 Deleting linked Objects Two realizations of the same structure: class Tag { Photo photo; CD association [*] Tag -> Photo [1]; As discussed: for consistency DEX generates additional xgenxmethods for the association and handle the associations as bidirectional DEX does that also for attributes, because when user deletes a Photo-object DEX also removes the links in all places where the object was referenced Decision for DEX: Deletion is always possible and null is a possible value in attributes as well as [1]-associations to classes (other than enums) defined in the CD. Page 54 Derived Association a derived association means (in analogy to a derived attribute): only retrieval functionality is provided in the interface, but with emtpy / useless implementation in the Impl-class this is to be handcoded (Chap. 3) association / member [*] Person <-> Group [*]; CD no manipulation methods association name Java interface Person { public boolean containsallmembers(collection<group> o); public boolean containsmember(group o); public boolean isemptymembers(); public Iterator<Group> iteratormembers(); public int sizemembers();
Page 55 Qualified Association a qualified association has same methods as unqualified assoc. plus methods from Map<X,Y> signature association [1] Person (organizer) <-> (organized) [profilename] Group [*]; target type key = attribute of type String interface Group { // boolean containsorganizer(person o); boolean containsorganizer(string key); role name boolean isemptyorganizers(); java.util.iterator<person> iteratororganizers(); int sizeorganizers(); java.util.set<string> keysetorganizer(); java.util.collection<person> valuesorganizer(); Person getorganizer(string key); void addorganizer(person o); void putorganizer(string key, Person o); void clearorganizers(); void removeorganizer(person o); Java Page 56 Ordered Association An <<ordered>> association provides List<X>-like signature More than 20 methods, here an excerpt: association sent [1] Profile <-> Post [*] <<ordered>> interface Profile { // boolean containssent(post o); boolean isemptysents(); int sizesents(); boolean addsent(post o); void addsent(int index, Post o); Post getsent(int index); int indexofsent(post o); int lastindexofsent(post o); void clearsents(); boolean removesent(post o); boolean removesent(int index); void setsent(int index, Post o); Java
Page 57 Ordered Association 2 More functionality: association sent [1] Profile <-> Post [*] <<ordered>> interface Profile { // boolean containsallsents(java.util.collection<post> o); Java Iterator<Post> iteratorsents(); ListIterator<Post> listiteratorsents(); ListIterator<Post> listiteratorsents(int index); boolean addallsent(collection<? extends Post> o); boolean addallsent(int index, Collection<? extends Post> o); boolean removeallsent(collection<post> o); List<Post> sublistsent(int start, int end); Page 58 Summary Associations This is the implementation and provided signatures: [1]-assoc. attribute + get/set [*]-assoc Set<X> + Set methods <<ordered>> List<X> + List methods qualified Map<qualifier,X> + Map methods DEX chooses to implement always bidirectional Consistency of bidirectional assocs is guaranteed Cardinality constraints are not guaranteed because DEX currently does not know how to handle violations DEX also provides search functions for [*] assocs
Page 59 Summary Lessons learned How to write class diagrams in CD lite How the DEX generator is started What DEX generates The architecture of the product Runtime environment vs. generated code How DEX maps classes and associations to code In particular we have seen: translation of associations has many facets the ratio output #loc / input #loc hints towards a tremendous speedup in generative software development