domain-specific languages in der praxis

17
User-Interface Programmierung mit externen DSLs Sven Efftinge, Jan Köhnlein (itemis AG) 1 DB-Anwendung Oracle DB OracleForms Ausgangslage 2

Upload: sven-efftinge

Post on 16-Jul-2015

784 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: Domain-Specific Languages in der Praxis

User-Interface

Programmierung mit

externen DSLs

Sven Efftinge, Jan Köhnlein (itemis AG)

1

• DB-Anwendung

• Oracle DB

• OracleForms

Ausgangslage

2

Page 2: Domain-Specific Languages in der Praxis

• Oracle DB

• Java Rich Client

• JPA

• Spring

• Swing / JGoodies Forms

Zielarchitektur

3

Mengengerüst

• 1722 Tabellen

• 19572 Spalten

• über 300 Forms

Aufgabe• Abstraktionen finden

• Code vereinfachen

4

Page 3: Domain-Specific Languages in der Praxis

Domänenmodel5

@SuppressWarnings("serial")

@Entity

@Table(name = "BUCHUNGSKREISE_F")

public class BuchungskreiseF extends AbstractEntity implements Serializable {

!

! @SuppressWarnings("unused")

! @Id

! @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bkrIdSeq")

! @SequenceGenerator(name = "bkrIdSeq", sequenceName = "BKR_SEQ", allocationSize = 1)

! @Column(name = "BKR_ID", nullable = false)

! private Long bkrId;

! public Long getBkrId() {

! ! return bkrId;

! }

! public void setBkrId(final Long bkrId) {

! ! this.bkrId = bkrId;

! }

! @Column(name = "KONTO_NR", nullable = false, length = 45)

! private String kontoNr;

! public String getKontoNr() {

! ! return kontoNr;

! }

! public void setKontoNr(final String kontoNr) {

! ! String oldValue = this.kontoNr;

! ! this.kontoNr = kontoNr;

! ! firePropertyChangeEvent("kontoNr", oldValue, this.kontoNr);

! }

! @Column(name = "RG_NR_BKR_IDENTIFIKATOR", nullable = false, length = 1)

! private Long rgNrBkrIdentifikator;

! public Long getRgNrBkrIdentifikator() {

! ! return rgNrBkrIdentifikator;

! }

! public void setRgNrBkrIdentifikator(final Long rgNrBkrIdentifikator) {

! ! Long oldValue = this.rgNrBkrIdentifikator;

! ! this.rgNrBkrIdentifikator = rgNrBkrIdentifikator;

! ! firePropertyChangeEvent("rgNrBkrIdentifikator", oldValue,

! ! ! ! this.rgNrBkrIdentifikator);

! }

! @Column(name = "REFERENZCODE_KONTO_NR", nullable = false, length = 45)

! private String referenzcodeKontoNr;

! public String getReferenzcodeKontoNr() {

! ! return referenzcodeKontoNr;

! }

! public void setReferenzcodeKontoNr(final String referenzcodeKontoNr) {

! ! String oldValue = this.referenzcodeKontoNr;

! ! this.referenzcodeKontoNr = referenzcodeKontoNr;

! ! firePropertyChangeEvent("referenzcodeKontoNr", oldValue,

! ! ! ! this.referenzcodeKontoNr);

! }

! @Column(name = "PC_NR_RG", nullable = false, length = 45)

! private String pcNrRg;

! public String getPcNrRg() {

! ! return pcNrRg;

! }

! public void setPcNrRg(final String pcNrRg) {

! ! String oldValue = this.pcNrRg;

! ! this.pcNrRg = pcNrRg;

! ! firePropertyChangeEvent("pcNrRg", oldValue, this.pcNrRg);

! }

! @Column(name = "PC_NR_PVA", nullable = false, length = 45)

! private String pcNrPva;

! public String getPcNrPva() {

! ! return pcNrPva;

! }

! public void setPcNrPva(final String pcNrPva) {

! ! String oldValue = this.pcNrPva;

! ! this.pcNrPva = pcNrPva;

! ! firePropertyChangeEvent("pcNrPva", oldValue, this.pcNrPva);

! }

! @Column(name = "MWST_NR", nullable = false, length = 10)

! private Long mwstNr;

! public Long getMwstNr() {

! ! return mwstNr;

! }

! public void setMwstNr(final Long mwstNr) {

! ! Long oldValue = this.mwstNr;

! ! this.mwstNr = mwstNr;

! ! firePropertyChangeEvent("mwstNr", oldValue, this.mwstNr);

! }

! @Column(name = "J_VERSION", nullable = false, insertable = false, updatable = false,

length = 22)

!

@org.hibernate.annotations.Generated(org.hibernate.annotations.GenerationTime.ALWAYS)

! @Version

! private Long jVersion;

! public Long getJVersion() {

! ! return jVersion;

! }

! public void setJVersion(final Long jVersion) {

! ! Long oldValue = this.jVersion;

! ! this.jVersion = jVersion;

! ! firePropertyChangeEvent("jVersion", oldValue, this.jVersion);

! }

! @Column(name = "IBAN", nullable = false, length = 34)

! private String iban;

! public String getIban() {

! ! return iban;

! }

! public void setIban(final String iban) {

! ! String oldValue = this.iban;

! ! this.iban = iban;

! ! firePropertyChangeEvent("iban", oldValue, this.iban);

! }

! @Column(name = "BUCHUNGSKREIS_NR", nullable = false, length = 4)

! private Long buchungskreisNr;

! public Long getBuchungskreisNr() {

! ! return buchungskreisNr;

! }

! public void setBuchungskreisNr(final Long buchungskreisNr) {

! ! Long oldValue = this.buchungskreisNr;

! ! this.buchungskreisNr = buchungskreisNr;

! ! firePropertyChangeEvent("buchungskreisNr", oldValue,

! ! ! ! this.buchungskreisNr);

! }

}

Entity

BuchungskreiseF

Id

Long bkrId

String kontoNr

nullable = false

nullable = false 45

BUCHUNGSKREISE_F

BKR_ID

KONTO_NR

BKR_SEQ

6

Page 4: Domain-Specific Languages in der Praxis

@SuppressWarnings("serial")

@Entity

@Table(name = "BUCHUNGSKREISE_F")

public class BuchungskreiseF extends AbstractEntity implements Serializable {

!

! @SuppressWarnings("unused")

! @Id

! @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bkrIdSeq")

! @SequenceGenerator(name = "bkrIdSeq", sequenceName = "BKR_SEQ", allocationSize = 1)

! @Column(name = "BKR_ID", nullable = false)

! private Long bkrId;

! public Long getBkrId() {

! ! return bkrId;

! }

! public void setBkrId(final Long bkrId) {

! ! this.bkrId = bkrId;

! }

! @Column(name = "KONTO_NR", nullable = false, length = 45)

! private String kontoNr;

! public String getKontoNr() {

! ! return kontoNr;

! }

! public void setKontoNr(final String kontoNr) {

! ! String oldValue = this.kontoNr;

! ! this.kontoNr = kontoNr;

! ! firePropertyChangeEvent("kontoNr", oldValue, this.kontoNr);

! }

! @Column(name = "RG_NR_BKR_IDENTIFIKATOR", nullable = false, length = 1)

! private Long rgNrBkrIdentifikator;

! public Long getRgNrBkrIdentifikator() {

! ! return rgNrBkrIdentifikator;

! }

! public void setRgNrBkrIdentifikator(final Long rgNrBkrIdentifikator) {

! ! Long oldValue = this.rgNrBkrIdentifikator;

! ! this.rgNrBkrIdentifikator = rgNrBkrIdentifikator;

! ! firePropertyChangeEvent("rgNrBkrIdentifikator", oldValue,

! ! ! ! this.rgNrBkrIdentifikator);

! }

! @Column(name = "REFERENZCODE_KONTO_NR", nullable = false, length = 45)

! private String referenzcodeKontoNr;

! public String getReferenzcodeKontoNr() {

! ! return referenzcodeKontoNr;

! }

! public void setReferenzcodeKontoNr(final String referenzcodeKontoNr) {

! ! String oldValue = this.referenzcodeKontoNr;

! ! this.referenzcodeKontoNr = referenzcodeKontoNr;

! ! firePropertyChangeEvent("referenzcodeKontoNr", oldValue,

! ! ! ! this.referenzcodeKontoNr);

! }

! @Column(name = "PC_NR_RG", nullable = false, length = 45)

! private String pcNrRg;

! public String getPcNrRg() {

! ! return pcNrRg;

! }

! public void setPcNrRg(final String pcNrRg) {

! ! String oldValue = this.pcNrRg;

! ! this.pcNrRg = pcNrRg;

! ! firePropertyChangeEvent("pcNrRg", oldValue, this.pcNrRg);

! }

! @Column(name = "PC_NR_PVA", nullable = false, length = 45)

! private String pcNrPva;

! public String getPcNrPva() {

! ! return pcNrPva;

! }

! public void setPcNrPva(final String pcNrPva) {

! ! String oldValue = this.pcNrPva;

! ! this.pcNrPva = pcNrPva;

! ! firePropertyChangeEvent("pcNrPva", oldValue, this.pcNrPva);

! }

! @Column(name = "MWST_NR", nullable = false, length = 10)

! private Long mwstNr;

! public Long getMwstNr() {

! ! return mwstNr;

! }

! public void setMwstNr(final Long mwstNr) {

! ! Long oldValue = this.mwstNr;

! ! this.mwstNr = mwstNr;

! ! firePropertyChangeEvent("mwstNr", oldValue, this.mwstNr);

! }

! @Column(name = "J_VERSION", nullable = false, insertable = false, updatable = false,

length = 22)

!

@org.hibernate.annotations.Generated(org.hibernate.annotations.GenerationTime.ALWAYS)

! @Version

! private Long jVersion;

! public Long getJVersion() {

! ! return jVersion;

! }

! public void setJVersion(final Long jVersion) {

! ! Long oldValue = this.jVersion;

! ! this.jVersion = jVersion;

! ! firePropertyChangeEvent("jVersion", oldValue, this.jVersion);

! }

! @Column(name = "IBAN", nullable = false, length = 34)

! private String iban;

! public String getIban() {

! ! return iban;

! }

! public void setIban(final String iban) {

! ! String oldValue = this.iban;

! ! this.iban = iban;

! ! firePropertyChangeEvent("iban", oldValue, this.iban);

! }

! @Column(name = "BUCHUNGSKREIS_NR", nullable = false, length = 4)

! private Long buchungskreisNr;

! public Long getBuchungskreisNr() {

! ! return buchungskreisNr;

! }

! public void setBuchungskreisNr(final Long buchungskreisNr) {

! ! Long oldValue = this.buchungskreisNr;

! ! this.buchungskreisNr = buchungskreisNr;

! ! firePropertyChangeEvent("buchungskreisNr", oldValue,

! ! ! ! this.buchungskreisNr);

! }

}

Entity

BuchungskreiseF

Id

Long bkrId

String kontoNr

nullable = false

nullable = false 45

BUCHUNGSKREISE_F

BKR_ID

KONTO_NR

BKR_SEQ

6

entity BuchungskreiseF (id=bkrId sequenceName=BKR_SEQ) {

String kontoNr

Long rgNrBkrIdentifikator

String referenzcodeKontoNr

}

BUCHUNGSKREISE_F

RG_NR_BKR_IDENTIFIKATOR

External DSL mit TMF Xtext

7

Page 5: Domain-Specific Languages in der Praxis

entity BuchungskreiseF (id=bkrId sequenceName=BKR_SEQ) {}

Database Schema

TABLE BUCHUNGSKREISE_F

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

8

9

Page 6: Domain-Specific Languages in der Praxis

Formulare

10

11

Page 7: Domain-Specific Languages in der Praxis

public class PersonenForm extends

Form<Personen> {

11

public class PersonenForm extends

Form<Personen> {

public class PersonenHauptSubForm extends

SubForm<Personen> {

11

Page 8: Domain-Specific Languages in der Praxis

public class PersonenForm extends

Form<Personen> {

public class PersonenHauptSubForm extends

SubForm<Personen> {

private JComponent

vornameTextField;

11

public class PersonenForm extends

Form<Personen> {

public class PersonenHauptSubForm extends

SubForm<Personen> {

private JComponent

vornameTextField;

@Override

protected void initComponents() {

...

vornameTextField =

builder.createTextField(desc.vorname(),

Editable.PROPERTY_DEFAULT,

MANDATORY);

! ! gepardBuilder.setNoLeadingBlanks

(vornameTextField);

11

Page 9: Domain-Specific Languages in der Praxis

public class PersonenForm extends

Form<Personen> {

public class PersonenHauptSubForm extends

SubForm<Personen> {

private JComponent

vornameTextField;

@Override

protected void initComponents() {

...

vornameTextField =

builder.createTextField(desc.vorname(),

Editable.PROPERTY_DEFAULT,

MANDATORY);

! ! gepardBuilder.setNoLeadingBlanks

(vornameTextField);

@Override

! protected JComponent buildPanel() {

TwoColumnsPanelBuilder builder =

TwoColumnsPanelBuilder.instance(getBuilderFactory(),

getResourceMap());

...

builder.add("vorname", vornameTextField);

!

11

GUI-Builder

• WYSIWYG

• teilweise mit Databinding

• zu viele Freiheitsgrade

• Referenz auf Java-Code,nicht auf Domain-DSL

12

Page 10: Domain-Specific Languages in der Praxis

Graphische GUI-DSL

13

Graphische GUI-DSL

13

Page 11: Domain-Specific Languages in der Praxis

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

ID NAME PROP1 DATE ATTR1

model : gepard;

import "platform:/resource/com.affichage.it21.gp.dao/src/main/model/types.dao"

com.affichage.it21.gp.dao {

! flaechen {

! ! readOnly entity WaehrungF (id =(rvLowValue)) {

! ! }

! ! readOnly entity GepardVerwendungPvF (id =(pvOid)) {

! ! ! temporal manyToOne GeschpartnerAllBsF geschpartner (joinColum

! ! }

! }

!

! verkauf {

! ! readOnly entity GepardVerwendungKdvtF (id =(kdvtId)) {

! ! ! temporal notNull manyToOne GeschpartnerAllBsF geschpartner (j

! ! ! notNull Number istLangfrist (castTo=Boolean)

! ! ! notNull Number istLokaldispo (castTo=Boolean)

! ! }

! }

! gepard {

! ! readOnly entity AbcKundenF (id = (rvLowValue)) {

! !

Referenzen

14

Validierung

15

Page 12: Domain-Specific Languages in der Praxis

16

addValidator(new Validator<Institutionen>() {

! ! ! @Override

! ! ! public ValidationResult validate(final Institutionen institution) {

! ! ! ! final ValidationResult result = new ValidationResult();

! ! ! ! if (institution != null

&& institution.getEsrNr() != null

! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(

Long.parseLong(institution.getEsrNr())) {

! ! ! ! ! result.add(new SimpleValidationMessage(

getResourceMap().getString("validation.esr.msg"),

Severity.ERROR, getModel(Institutionen.DESC.esrNr())));

! ! ! ! }

! ! ! ! return result;

! ! ! }

! ! });

17

Page 13: Domain-Specific Languages in der Praxis

addValidator(new Validator<Institutionen>() {

! ! ! @Override

! ! ! public ValidationResult validate(final Institutionen institution) {

! ! ! ! final ValidationResult result = new ValidationResult();

! ! ! ! if (institution != null

&& institution.getEsrNr() != null

! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(

Long.parseLong(institution.getEsrNr())) {

! ! ! ! ! result.add(new SimpleValidationMessage(

getResourceMap().getString("validation.esr.msg"),

Severity.ERROR, getModel(Institutionen.DESC.esrNr())));

! ! ! ! }

! ! ! ! return result;

! ! ! }

! ! });

Framework-Code

17

addValidator(new Validator<Institutionen>() {

! ! ! @Override

! ! ! public ValidationResult validate(final Institutionen institution) {

! ! ! ! final ValidationResult result = new ValidationResult();

! ! ! ! if (institution != null

&& institution.getEsrNr() != null

! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(

Long.parseLong(institution.getEsrNr())) {

! ! ! ! ! result.add(new SimpleValidationMessage(

getResourceMap().getString("validation.esr.msg"),

Severity.ERROR, getModel(Institutionen.DESC.esrNr())));

! ! ! ! }

! ! ! ! return result;

! ! ! }

! ! });

Framework-Code

Nullpointer abfragen

17

Page 14: Domain-Specific Languages in der Praxis

addValidator(new Validator<Institutionen>() {

! ! ! @Override

! ! ! public ValidationResult validate(final Institutionen institution) {

! ! ! ! final ValidationResult result = new ValidationResult();

! ! ! ! if (institution != null

&& institution.getEsrNr() != null

! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(

Long.parseLong(institution.getEsrNr())) {

! ! ! ! ! result.add(new SimpleValidationMessage(

getResourceMap().getString("validation.esr.msg"),

Severity.ERROR, getModel(Institutionen.DESC.esrNr())));

! ! ! ! }

! ! ! ! return result;

! ! ! }

! ! });

Framework-Code

Nullpointer abfragen

Static imports

17

addValidator(new Validator<Institutionen>() {

! ! ! @Override

! ! ! public ValidationResult validate(final Institutionen institution) {

! ! ! ! final ValidationResult result = new ValidationResult();

! ! ! ! if (institution != null

&& institution.getEsrNr() != null

! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(

Long.parseLong(institution.getEsrNr())) {

! ! ! ! ! result.add(new SimpleValidationMessage(

getResourceMap().getString("validation.esr.msg"),

Severity.ERROR, getModel(Institutionen.DESC.esrNr())));

! ! ! ! }

! ! ! ! return result;

! ! ! }

! ! });

Framework-Code

Nullpointer abfragen

Static imports

Bibliotheks Methoden definieren und benutzen

error("validation.esr.msg", desc.esrNr());

17

Page 15: Domain-Specific Languages in der Praxis

! void checkEsrMsg() {

! ! if (!checkPcKontoNrPruefziffer(parseLong(_this.getEsrNr())))

! ! ! error("validation.esr.msg",desc.esrNr());

! }

Internal DSL mit Java

• Junit-like

• fängt Nullpointer-Exceptions

18

Warum Abstrahieren?

19

Page 16: Domain-Specific Languages in der Praxis

0 TZ

60 TZ

120 TZ

180 TZ

240 TZ

300 TZ

0 20 40

29248 (DSL)+ 49948 (Codegenerator)+ 170 * Anzahl Entitäten

7303 * Anzahl Entitäten

Anzahl Zeichen im Vergleich (nur Entity-DSL)

Mit DSL

Ohne DSL

11 Entitäten

20

67%

8%

7%

5%

6%5%2%

RequirementsSpecificationDesignCodingUnit TestingIntegrationMaintenance

Software Life-Cycle Costs (Schach 2002)

21

Page 17: Domain-Specific Languages in der Praxis

Fragen?

22