Session description from Code Generation 2009:
Deutsche Boerse AG (www.deutsche-boerse.com) is developing a so-called global trading system, where Eclipse Modeling Technology is used in different parts. From diverse models, C++, Java & Python adapters and other different artefacts are generated to build a mission-critical and highly-scalable software system.
In this session we will describe how openArchitectureWare helped us to build a tool chain that spans different subprojects and connects existing modeling tools, be they graphical, tabular or textual. In an international team we were able to build custom parts or re-use slightly adjusted community cartridges by applying AOP techniques to build M2M transformations, sophisticated editors as well as validation and server-side code generators.
3. The project at
Deutsche Börse
! Projects delivers a “Global Trading System”
! First (internal) user is ISE in NY
! Scalable, Distributed System
! Goes live in 2011
4. Structure of this talk
! 8 times
Motivation ! Applied Approach
! Inbetween: Demo of discussed ideas
! In the end: Some discoveries we made
http://www.bildtankstelle.de
6. Reference Configuration Messaging Excel Model
Data Model Model Model
Scripting
Generator
System
Reference Test
Messaging
Data Generator
Generator
Generator
Structures Structures
Messages Messages Tests
Validation IML
JPA Entities OFi Messages Messages Docu
Listeners Datatypes C++ Python Python
Model
Val.List.
DAOs UnitTest Structures Structures
XML <<uses>>
DTOs
IML Msg/Struct.
Model
Listener Factories Editor
Adapters
UOW Exec. Persistence HTML,
Bean Descriptor C++ Java Text XML
Msg Client WSFacade Report <<uses>>
Descriptor <<uses>> <<uses>>
Java HTML
Transactional
Container Model Model
.Net
Hibernate JAXB Data Generator
Access DB
Generator Service
WSFacade
Generator
DDL Scripts XSD
SQL XML
Global key Messages
Filters DDL Scripts
functions
Ref Data UnitTest DDL Scripts
Containers Queries Structures
Mngr Class Templates (MySQL)
Boost DAOs Factories
Operator
C++ SQL C++ SQL .Net
7. Artifact sizes
Generated
Transformation
Model
Tests (manual)
Sources (manual)
Reference Impl.
2.747 units
Actual numbers had
to be replaced by
fictive “units”
9. Why Code Generation?
! Positive experience with former projects
! Prime example: mapping between
messages and structures sending with a
compact binary wire protocols
Component Component
Component Structure B
B1
A1 A3 A2 B2
Structure B
B1 SubStructure S
B1 S1 B2 S2
B2 S1
SubStructure S S2
S1
S2
10. Why openArchitectureWare?
Former times:
! Home-grown solution based on graphical modeling
and proprietary tools
! Many different post-processors such as XSLT and Perl
Evaluation results of oAW for GTS
! Supports textual modeling
! Has mature tool support
! Works with different model types and output formats
11. Different Input , Different Output ,
Same Transformation Language
Text UML C++ Java
Xpand
XML XMI XML XMI
12. When in Rome
do as the Romans do
Motivation: Address existing experience
and perception
13. Modeling in UML
! Years of experience for modeling entities
graphically (ER-Diagram)
! Established Tool (MagicDraw)
! Proven Technology (Profiles, Tagged
Values)
! Great for overview perspectives
15. Textual Modeling
! Years of experience ;)
! Established tool support (grep, editor,
mail, svn)
! Highly customizable ! DSL
! Inexpensive metamodel evolvement
! Great for detailed modeling
18. Reducing Model
Complexity
! Following Convention-over-configuration
paradigm for modeling
! Only model necessary information
! Complete model according to
conventions by M2M transformation
20. Convention: Navigable association ends without
name get the name of the referred Entity. Use plural for to
many relationships.
uml::Model modify (uml::Model this) :
... ->
// Search for unnamed association ends and give them a default name
this.eAllContents.typeSelect(Property)
.select(x|x.association!=null).nameUnnamedEnds() ->
...
/**
* This function sets a default name for an association's end property if the property
* has no name and it is navigable.
*/
Void nameUnnamedEnds(Property this) :
if
!isNamed() && isNavigable()
then (
("setting default name for association end "+opposite.type.name+" -> "+type.name).info() ->
setName(computeDefaultName())
)
-> this;
22. Convention: All entities without <<Key>> attribute get a
new property with name id of type Long, and Stereotype
<<Key>> is applied.
uml::Model modify (uml::Model this) :
... ->
// Search for unnamed association ends and give them a default name
this.eAllContents.typeSelect(Property)
.select(x|x.association!=null).nameUnnamedEnds() ->
...
private create uml::Property createTechnicalId(RDMProfile::Entity entity):
("Creating technical id attribute for entity "+entity.name).debug() ->
entity.ownedAttribute.addToFront(this) ->
this.setName("id") ->
this.setType(findDataType(„Long“) ->
this.setLower(1) ->
this.setUpper(1) ->
this.setIsUnique(true) ->
this.setVisibility(uml::VisibilityKind::^private) ->
this.applyStereotype("RDMProfile::Key") ->
this.setTaggedValue("RDMProfile::Key", "keyType", RDMProfile::KeyType::PK)->
this.applyStereotype("RDMProfile::Field") ->
this.setTaggedValue("RDMProfile::Field", "uniqueGroup", "444")->
this;
23. Combination of model types
Domain model
UML2
platform independent
Configuration model
textual DSL
platform dependent
entity Market
{
relatedTo MarketAbstract with EAGER loading and cascade with PERSIST
relatedTo MarketGroup with LAZY loading and cascade with PERSIST
}
24. Combination of model types
entity Market
{
relatedTo MarketAbstract with EAGER loading and cascade with PERSIST
relatedTo MarketGroup with LAZY loading and cascade with PERSIST
}
public class Market implements RDSEntity, Serializable {
...
@OneToMany(mappedBy = "market", cascade = CascadeType.PERSIST)
@Basic(fetch = FetchType.EAGER)
private List<MarketAbstract> marketAbstracts =
new ArrayList<MarketAbstract>();
@ManyToMany(cascade = CascadeType.PERSIST, targetEntity = MarketGroup.class,
mappedBy = "markets")
@Basic(fetch = FetchType.LAZY)
private List<MarketGroup> marketGroups = new ArrayList<MarketGroup>();
25. Avoidable Accidents
Motivation:
Detect errors early
(c) Ernest von Rosen, www.amgmedia.com
26. l e t :
t ityRe = -1).siz emberEnd. t set)!"
e::En pper =
ofil |e.u " + m ion is no (
RD MPr e at ion at > 1 &&
con text d.select( n y rel rOfAssoci )) .size )!="n
er En y to ma (o wne Ac tive( String(
Detecting errors early
(memb NING "Man relation ll; t(e |e.is tion.to " must
b
WAR != nu ec a
of the on p e.sel |e.aggreg .info() + not appl
s o wner sso ciati nd.ty lect(e
berE d.se
e
e.nam tereotype
a fA m p
o wnerO n if me mberEn rE nd.ty ion (s
c iatio ) && me + membe e relat
:Asso e == 2
uml: .siz ati on " of th ;
ntext == -1) el er
co to m any r es as own nce(this) ll) an en
t
er y it i ta nu s
e.upp NING "Man f the ent ion.isIns o n == e rence
WAR ne o el at so ciati ame+" ref
ine o ::EntityR & (as class.n
a ! Check constraints implemented for
f
nd de rofile
DMP .isAc
t
in en
&
ive() tity "+
R lass =
y if c m e + " f ault!
Propert ty " + na ); & & ^de fault
con text ! Every assumption or modeling restriction
"Pro
ERROR n!":
per
isIns
ta nce (type
ciati
on == null
t nul
l ) as
de
ntity
. so no
ia tio :E && as r ing (
assoc MProfile: ive() p ty st
!RD ! Modeling error that lead to generator or
as s.isA
if cl loc()+" h
ct
as an em
y
st ance(
.isIn ce it i
c
perty rty "+ Entit y, sin
cont artifact errors (e.g. Whitespace in names)
ext P
ro
ING "
Pr ope Pr
& RDM typed a
::
ofile s Entit
WARN ve()
& o
s Acti stere
""; ass.i " must be :
=
! Inter-model consistency checks
op ert y if
cl
pe
+
.name y "+name ); In stanc
e(
nte xt Pr lass "+ty propert f(t ype nti ty.is are a
l
co C ce :E s
ER ROR " +" in its .isInstan o file: umeration
! Risk of modeling errors decreased
.n ame ti ty & RDMPr nal En
"+ class ofile::En iv e() & Functio
RD MPr . isAct pes or
if class Dataty ype)
||
y t
Pr opert unctional st ance( pe);
con text nl y F pe .isIn ance(ty
ERR OR "O "+loc() : nalDataTy .isInst
n
te d for ::Functio numeratio
Viola rofil
e
ional
E
27. Constraints with Check language
context uml::Association
if memberEnd.type.select(e|e.isActive()).size>1
&& (memberEnd.select(e|e.upper == -1).size == 2)
&& memberEnd.select(e|e.aggregation.toString()!="none").size == 2
WARNING "Many to many relation " + memberEnd.type.name.info() +
" must be of type EntityRelation and define one of the entities" +
" as owner of the relation (stereotype not applied)!" :
RDMProfile::EntityRelation.isInstance(this);
context Property if class.isActive() && (association == null)
ERROR "Property " + name + " in entity "+class.name +
" references an entity, but is not an association!":
!RDMProfile::Entity.isInstance(type);
context Property
if class.isActive() && association == null && ^default!=null
WARNING "Property "+loc()
+" has an empty string (not null) as default value"
: ^default.trim()!="";
28. Integration
! Static Typing while developing
checks and transformations
! IDE integration for workflows invocation
! Continuous Generation (server-side)
CMake / Ant / Maven
! Automated execution after SVN checkout
30. Standing on the
shoulders of giants
Motivation:
Reusing generator aspects
31. Cartridge adaption
! Using OS cartridges as Cartridge
Workflow
DTO
Template
WebService
Template
UnitTest
Template
closed package
Project Cartridge
<<call>>
! Required model structure
created through M2M
Adapter Aspect M2M
Workflow Template Transformation
transformation Fornax Adapter Cartridge
<<call>> <<redefines>>
! Required changes added Cartridge Annotation EntityClass Mapping
non-invasive through
Workflow Template Template Template
Xpand/Xtend AOP support Fornax Hibernate Cartridge
<<call>> <<redefines>>
! Redefined functionality Cartridge
Workflow
Attribute
Template
Class
Template
Operation
Template
concentrated in a small
Fornax JavaBasic Cartridge
adapter cartridge project
32. UML2 model modification with Xtend
uml::Model modify (uml::Model this) :
this.applyProfile(getPersistenceProfile()) ->
// Create real enumerations for FunctionalEnumerations. This must be done first since
// we map datatypes after that
this.eAllContents.typeSelect(RDMProfile::FunctionalEnumeration).adaptEnumeration() ->
entities().adaptEntity() ->
adaptEmbeddedKeys() ->
this;
RDMProfile::Entity adaptEntity (RDMProfile::Entity entity) :
debug("Adapting entity: "+ entity.name) ->
entity.applyStereotype("Persistence::Entity") ->
entity.setTaggedValue("Persistence::Entity", "tableName", entity.name.asTableName())->
entity.testAndAssignInheritanceStrategy()->
entity.attribute.select(a|a.association==null).adaptAttribute() ->
entity.attribute.select(a|a.association!=null).adaptRelation() ->
entity;
//In case the entity extends another class, assigne the persistence stereotype for
//strategy on the uml::Generalization
//TPC="TABLE_PER_CLASS" | J="JOINED" | ST="SINGLE_TABLE";
Void testAndAssignInheritanceStrategy(RDMProfile::Entity this):
if !general.isEmpty
then assignInheritanceStrategy();
Void assignInheritanceStrategy(RDMProfile::Entity this):
let gener = generalization.first():
gener.applyStereotype("Persistence::"+getInheritanceStrategyFromDardl().mapToStrategy()) ->
if(getInheritanceStrategyFromDardl() == "SINGLE_TABLE")
then (
gener.setTaggedValue("Persistence::TablePerClassHierarchy",
"discriminator_value", getDiscriminatorValue())->
gener.setTaggedValue("Persistence::TablePerClassHierarchy",
"discriminator_superClass_column", gener.general.name+"_type")->
gener.setTaggedValue("Persistence::TablePerClassHierarchy",
"discriminator_superClass_type", "INTEGER")
)
;
33. AOP with templates and functions
«AROUND org::fornax::cartridges::uml2::javabasic::templates::Documentation::documentation
FOR uml::Classifier»
«targetDef.proceed()»
«EXPAND JPAAnnotations»
«ENDAROUND»
«DEFINE JPAAnnotations FOR Persistence::Entity»
@javax.persistence.Entity
@javax.persistence.Table(name="«asTableName()»")
«EXPAND InheritanceAnnotations»
@javax.persistence.NamedQueries({
@javax.persistence.NamedQuery(name="find«name»s", query="SELECT e from «name» e")
})
«IF !isAbstract»
@javax.persistence.EntityListeners
(«getQualifiedPackageName("ListenerOnce")».«name»ValidationListener.class)
«ENDIF»
«EXPAND UniqueConstraints»
«ENDDEFINE»
around org::fornax::cartridges::uml2::javabasic::extensions::DataType::NormalizedDefaultValue(
uml::Property property): internal_getNormalizedDefaultValue(property);
private String internal_getNormalizedDefaultValue (uml::Property property) :
JAVA gts.ise.refdata.oaw.fornax.util.Extensions.getNormalizedDefaultValue(org.eclipse.uml2.uml.Property);
38. Reference Implementation
! Manually implemented
! Far more than a prototype!
! Compilable, deployable, executable, testable
! Covers every architectural concept
! Code seperated in to-be-generated and
manual code
39. Reference Model
! Contains every supported modeling concept
! Not necessary excerpt from the real domain,
but helps for understanding
! Try to minimize the model size
40. Testing the reference
! Reference Implementation is tested
intensively and automatic
! Unit tests and integration tests with high
coverage
! Tests against generated artifacts
! Real application does not need to be
tested in the same extend
51. Most significant problems
! Repeated calls of functions
! Suboptimal algorithms/data structures
! Branches with best-case scenarios
! Profiling reduced generation time
from 28 min downto 3 min
53. Value of Cartridges
! Project benefited from cartridges
developed by OS community
! But: Largest part of the code generators
are totally project dependent
54. Early adopters
! Using the bleeding edge of Eclipse
Modeling was challenging
! But: Continuous improvement of used
technologies thanks to direct feedback
with OS community
! And: Significant performance and
stability gains since early adoption
55. Learning curve
! Establishing MDSD and tools was hard
for unexperienced developers at the
beginning
! But: Coaching helped to fill the gap fast
! And: Now developers educate others to
use MDSD tooling
56. Reference Implementation
! Reference Models and Reference
Implementation are invaluable
! Reducing the complexity
! Enable refactoring
! But: Effort to create and maintain a real
Reference Implementation quite high