XMAdsl Manual Karl Hönninger Michael Clay Johannes Smutny

Transcription

XMAdsl Manual Karl Hönninger Michael Clay Johannes Smutny
XMAdsl Manual
Karl Hönninger
Michael Clay
Johannes Smutny
Kurosch Aliabadi
Johannes Murth
Vikas Gupta
Published 18.09.2014 11:29
Copyright © 2009 s IT Solutions
Table of Contents
Preface ...................................................................................................................... x
1 Introduction ......................................................................................................... x
2 Versions .............................................................................................................. x
1 Installation .............................................................................................................. 1
1.1 User - Setup ...................................................................................................... 1
1.1.1 Java 5 ......................................................................................................... 1
1.1.2 Maven 3.x ................................................................................................... 1
1.1.3 Eclipse 3.7.x ................................................................................................ 1
1.1.4 openXMA SDK 5.x ..................................................................................... 1
1.2 Developer - Setup ............................................................................................. 1
1.2.1 Java 5 ......................................................................................................... 1
1.2.2 Maven 3.x ................................................................................................... 1
1.2.3 Eclipse 3.7 .................................................................................................. 2
1.2.4 Subversive SVN Team Provider ................................................................... 2
1.2.5 Import Team Project Set .............................................................................. 2
1.2.6 Workspace Configuration ............................................................................. 3
1.3 Project Reference .............................................................................................. 8
1.3.1 Aera 1 - Language ....................................................................................... 8
1.3.2 Area 2 - Generator ....................................................................................... 9
1.3.3 Area 3 - Clients ........................................................................................... 9
2 First Example ........................................................................................................ 10
2.1 Create a new project ........................................................................................ 10
2.2 Run the Generator ........................................................................................... 17
2.2.1 Main toolbar .............................................................................................. 18
2.2.2 Context menu ............................................................................................ 20
2.2.3 Standalone ................................................................................................. 22
2.3 Start Application ............................................................................................. 22
2.4 Complete domain model .................................................................................. 26
2.4.1 Create a new CustomerDao query operation ................................................ 27
2.4.2 Adapt CustomerDas Service ....................................................................... 29
2.4.3 Invoke the query operation ......................................................................... 29
2.4.4 Restart the Application ............................................................................... 29
3 Overview .............................................................................................................. 31
3.1 General ........................................................................................................... 31
3.2 Technology ..................................................................................................... 31
3.3 Modular (textual) models ................................................................................. 33
3.4 Layered models ............................................................................................... 35
3.5 Integrated models ............................................................................................ 36
4 Presentation Layer Modeling .................................................................................. 39
4.1 Approach of modeling the presentation layer ..................................................... 39
4.1.1 Presentation model ..................................................................................... 39
4.1.2 Design model ............................................................................................ 39
4.1.3 Referencing the domain model ................................................................... 40
4.2 Presentation model introduction ........................................................................ 40
4.2.1 Structuring the presentation layer ................................................................ 40
Components .................................................................................................... 41
Pages .............................................................................................................. 41
4.2.2 Hello world example .................................................................................. 41
Create a component ......................................................................................... 41
Create a page .................................................................................................. 42
ii
XMAdsl Manual
4.2.3 How to model a page .................................................................................
Data objects ....................................................................................................
Content ...........................................................................................................
Commands ......................................................................................................
Menus .............................................................................................................
Event mapping ................................................................................................
Conditions .......................................................................................................
Logic blocks ...................................................................................................
4.3 Formatters and Validators ................................................................................
4.3.1 Referencing formatters in PML ...................................................................
4.3.2 Concept .....................................................................................................
4.3.3 Validation ..................................................................................................
4.3.4 Formatting .................................................................................................
4.3.5 Custom formatters ......................................................................................
4.3.6 Writing own custom formatters ...................................................................
4.4 Component ......................................................................................................
4.4.1 Modeling a component ...............................................................................
4.4.2 Data object variables ..................................................................................
4.4.3 Commands ................................................................................................
4.4.4 Event mapping ...........................................................................................
4.4.5 Conditions .................................................................................................
4.4.6 Definitions .................................................................................................
4.4.7 Details for Core/SWT applications ..............................................................
4.5 Pages ..............................................................................................................
4.5.1 Page ..........................................................................................................
4.5.2 Referenced page of openXMA core ............................................................
4.5.3 Details for Core/SWT applications ..............................................................
4.6 Simple GUI elements .......................................................................................
4.6.1 Label .........................................................................................................
4.6.2 Text ..........................................................................................................
4.6.3 Combo ......................................................................................................
4.6.4 Checkbox ..................................................................................................
4.6.5 Radiobutton ...............................................................................................
4.6.6 Seperator ...................................................................................................
4.6.7 Tree ..........................................................................................................
4.7 Panels .............................................................................................................
4.7.1 Panel .........................................................................................................
4.7.2 Vertical panel ............................................................................................
4.7.3 Horizontal panel ........................................................................................
4.8 Field ...............................................................................................................
4.9 Table ..............................................................................................................
4.9.1 Table body ................................................................................................
4.9.2 Table column .............................................................................................
4.9.3 Table combo ..............................................................................................
4.10 Tab folder and tab pages ................................................................................
4.10.1 Tab folder ................................................................................................
4.10.2 Tab page .................................................................................................
4.11 Layout ...........................................................................................................
4.11.1 Tabulator .................................................................................................
4.11.2 Attachment ..............................................................................................
4.12 Styles ............................................................................................................
4.12.1 Defining styles .........................................................................................
iii
43
43
44
45
47
48
48
49
49
50
50
50
51
51
52
53
53
53
53
54
54
54
55
55
55
55
56
56
56
56
57
57
57
58
58
58
58
59
59
60
60
60
61
61
62
62
62
62
62
63
63
63
XMAdsl Manual
4.12.2 Applying styles ........................................................................................ 64
4.13 Extending and customizing a page .................................................................. 64
5 Domain Model ...................................................................................................... 65
5.1 Entity .............................................................................................................. 65
5.1.1 Comment ................................................................................................... 66
5.1.2 Identifier ................................................................................................... 67
5.1.3 Version ..................................................................................................... 68
5.1.4 Attributes .................................................................................................. 68
5.1.5 References ................................................................................................. 69
Association ..................................................................................................... 69
Composition .................................................................................................... 70
Reference examples ......................................................................................... 71
5.1.6 Natural Key ............................................................................................... 73
5.1.7 Unique Keys .............................................................................................. 73
5.1.8 Sort Orders ................................................................................................ 73
5.1.9 Generated Artefacts .................................................................................... 73
5.1.10 Syntax Diagram ....................................................................................... 76
5.2 Repository ....................................................................................................... 77
5.2.1 Operation .................................................................................................. 79
5.2.2 Query Operations ....................................................................................... 79
5.2.3 Callable Statement Operation ...................................................................... 81
5.2.4 Columns .................................................................................................... 86
5.2.5 Many-to-one .............................................................................................. 87
5.2.6 One-to-many .............................................................................................. 87
5.2.7 One-to-one ................................................................................................ 88
5.2.8 Generated Artefacts .................................................................................... 88
5.2.9 Syntax Diagram ......................................................................................... 91
5.3 ValueObject .................................................................................................... 92
5.3.1 Generated Artefacts .................................................................................... 92
5.3.2 Syntax Diagram ......................................................................................... 94
5.4 DataView ........................................................................................................ 95
5.4.1 Generated Artefacts .................................................................................... 97
5.4.2 Syntax Diagram ......................................................................................... 99
5.5 Mapper .......................................................................................................... 100
5.5.1 DataType Conversion ............................................................................... 102
5.5.2 How To Call A Mapper ........................................................................... 104
5.6 Service .......................................................................................................... 105
5.6.1 Naming ................................................................................................... 107
5.6.2 Dependencies ........................................................................................... 107
5.6.3 Operations ............................................................................................... 107
5.6.4 Dataaccess Operations .............................................................................. 107
5.6.5 Generated Artefacts .................................................................................. 110
5.6.6 Syntax Diagram ....................................................................................... 112
5.6.7 Service Layer Infrastructure ...................................................................... 113
Generic Exceptions ........................................................................................ 113
Specialized Application Exceptions and Helpers .............................................. 113
Validation with Spring Validators ................................................................... 113
Unique Constraints ........................................................................................ 115
Error Message Resolution .............................................................................. 116
5.7 DataType ....................................................................................................... 117
5.8 Enum ............................................................................................................ 117
5.9 Bean Validation ............................................................................................. 118
iv
XMAdsl Manual
5.9.1 Validating an object ................................................................................. 120
5.9.2 Error message resolution .......................................................................... 121
Default messages ........................................................................................... 121
6 Generator ............................................................................................................ 123
6.1 Properties ...................................................................................................... 123
6.1.1 Additional Optional Features .................................................................... 125
6.2 Templates ...................................................................................................... 125
6.3 Output configuration ...................................................................................... 128
6.4 NamingStrategy ............................................................................................. 129
6.5 ModelModifier .............................................................................................. 131
6.6 Hibernate Properties ....................................................................................... 132
6.7 Service Layer Properties ................................................................................ 132
6.8 Starting with Maven ...................................................................................... 134
6.9 Starting from eclipse ...................................................................................... 135
6.10 Generator Patterns ........................................................................................ 138
6.11 Workflow .................................................................................................... 138
7 Tooling ............................................................................................................... 141
7.1 Ddl Import Wizard ........................................................................................ 141
7.2 Navigation ..................................................................................................... 143
7.3 Views ............................................................................................................ 143
7.4 Diagrams ....................................................................................................... 143
8 Customization ..................................................................................................... 144
8.1 Spring Configuration: Autowire vs. XML beans .............................................. 144
8.2 Using XProperties .......................................................................................... 144
8.2.1 Using XProperties in Spring's application context ....................................... 144
9 Paging and customizing Tables ............................................................................ 146
9.1 PagingControl ................................................................................................ 146
9.1.1 General Description .................................................................................. 146
9.1.2 Modeling the PagingControl ..................................................................... 146
9.1.3 TableCustomizer ...................................................................................... 148
DDL for the TableCustomizer ................................................................ 151
9.1.4 DDL for the TableCustomizer ........................................................... 151
9.2 TableCustomizer Context Menu ...................................................................... 151
9.2.1 Configuring the menu ............................................................................... 152
9.3 Configuration Needed for the TableCustomizer ................................................ 152
9.4 Export of Table Data into a .csv File ............................................................... 153
9.4.1 Configuration needed for the .csv export functionality ................................ 154
10 JSF Generator .................................................................................................... 156
10.1 Prerequisites ................................................................................................ 156
10.2 Creating a new XMAdsl JSF project with Wizard .......................................... 156
10.3 JSF Error Message Presentation .................................................................... 161
10.4 JSF Simple AJAX calls ................................................................................ 162
10.5 Internationlization ........................................................................................ 162
10.6 AJAX Error Handling .................................................................................. 162
10.7 JSF UI Customization .................................................................................. 163
10.8 JSF UI Automation Testing .......................................................................... 164
10.9 JSF Invocation ............................................................................................. 168
10.10 JSF Performance Testing ............................................................................ 169
10.11 JSF Performance Best Practices .................................................................. 171
10.12 JSF IDE .................................................................................................... 171
10.13 JSF Logging .............................................................................................. 172
10.14 XSS Protection .......................................................................................... 172
v
XMAdsl Manual
10.15 URL Rewriting ..........................................................................................
10.16 CSRF Protection ........................................................................................
10.17 Validations ................................................................................................
10.17.1 Server Side Validations .........................................................................
10.17.2 Error Message Resolution .....................................................................
10.17.3 Partial Validations ................................................................................
10.18 Visible Flag ...............................................................................................
10.19 Full Page Navigation ..................................................................................
10.20 Remote Debugging .....................................................................................
10.21 JSF datatable .............................................................................................
10.22 BootStrap Navbar Component .....................................................................
10.23 Toggle Button ............................................................................................
Glossary .................................................................................................................
Bibliography ..........................................................................................................
vi
173
173
175
175
175
176
176
176
176
177
178
178
180
181
List of Figures
1.1 Team Project Set .................................................................................................. 3
1.2 M2_REPO Classpath Variables ............................................................................. 6
1.3 M2_REPO Linked Resource Path Variable ............................................................ 7
2.1 Demo Model ...................................................................................................... 10
2.2 DSL Project ....................................................................................................... 11
2.3 New openXMA Project ....................................................................................... 12
2.4 openXMA Project Source Settings ....................................................................... 13
2.5 openXMA Project Libraries Settings .................................................................... 14
2.6 Project Structure ................................................................................................. 16
2.7 Run Generator from main toolbar ........................................................................ 19
2.8 Run generator from context menu ........................................................................ 21
2.9 Customer SearchForm ......................................................................................... 24
2.10 XMA_HOME ................................................................................................... 25
2.11 XMA_HOME String Substitution ...................................................................... 26
2.12 Open Xtext Element ......................................................................................... 28
2.13 CustomerSearchForm with database search results .............................................. 30
3.1 Model driven software development .................................................................... 31
3.2 Layered Archictecture ......................................................................................... 35
3.3 Layered Models .................................................................................................. 36
3.4 Integrated models ............................................................................................... 38
4.1 Models containing information about the presentation layer ................................... 39
4.2 UI definition model and editor ............................................................................ 40
4.3 Component models ............................................................................................. 41
4.4 Select component creation wizzard ...................................................................... 41
4.5 Define the package and component name ............................................................. 42
4.6 Component model files ....................................................................................... 42
4.7 Presentation model for empty Component ............................................................ 42
4.8 Component with empty page ............................................................................... 42
4.9 Component with hello world page ....................................................................... 43
4.10 Design model of hello world page ..................................................................... 43
4.11 Customer data objects ....................................................................................... 44
4.12 Content with customer attributes ........................................................................ 44
4.13 Content preview ............................................................................................... 45
4.14 Commands ....................................................................................................... 45
4.15 Simple Page Invocation ..................................................................................... 45
4.16 Proxy Definition and Invocation ........................................................................ 46
4.17 Embedding ....................................................................................................... 46
4.18 Embedding ....................................................................................................... 46
4.19 Menus .............................................................................................................. 47
4.20 Event mapping ................................................................................................. 48
4.21 Conditions and button enabling ......................................................................... 49
4.22 Logic blocks, conditions and widget visibility .................................................... 49
4.23 formatting/verifying data flow ........................................................................... 50
4.24 validation event flow ........................................................................................ 50
4.25 formatting event flow ........................................................................................ 51
5.1 MDD ................................................................................................................. 65
5.2 Entity Structure .................................................................................................. 66
5.3 Entity with documentation .................................................................................. 67
5.4 Entity artefacts ................................................................................................... 75
5.5 Provider artefacts ................................................................................................ 90
vii
XMAdsl Manual
5.6 ValueObject Structure ......................................................................................... 92
5.7 ValueObject artefacts .......................................................................................... 93
5.8 DataView artefacts ............................................................................................. 98
5.9 ......................................................................................................................... 100
5.10 ....................................................................................................................... 101
5.11 ....................................................................................................................... 101
5.12 ....................................................................................................................... 102
5.13 ....................................................................................................................... 105
5.14 Service Structure ............................................................................................. 106
5.15 Service artefacts .............................................................................................. 111
6.1 Domain model generator workflow .................................................................... 139
9.1 Sample figure of table using the PagingControl .................................................. 146
9.2 Sample Screenshot of the TableCustomizer dialog .............................................. 148
9.3 Table context menu .......................................................................................... 151
9.4 Table context menu (generic) ............................................................................ 151
9.5 Button to handle csv export ............................................................................... 153
viii
List of Tables
4.1 FmtFactory patterns ............................................................................................ 51
10.1 ....................................................................................................................... 174
ix
Preface
1 Introduction
The main goals of XMAdsl are
• Be a text modelling based approach to simplify data related user interface
development tasks like
• Screen variations (similar screens with same data)
• Reuse of data field related properties like labels, tooltips...
• Simple standard layout
• Conditional display/hide of fields (including a proper layout adjustment)
• Offer a (also text modeling based) service layer
• Modeling clean and reusable interfaces
• Including database access generation
• Support dynamic (at production time) creatable fields
• In database
• On user interface
2 Versions
0.1
x
Chapter 1: Installation
1.1 User - Setup
1.1.1 Java 5
If you dont have a Jdk 5 or newer, please download and install a jdk 5 version from sun1.
1.1.2 Maven 3.x
Install maven version 3.0.3 or better from here2 and configure the following environment
variables
•
•
•
•
M2_HOME (points to you local maven installation)
JAVA_HOME (point to your JDK 5 installation)
add %M2_HOME%/bin to your PATH environment variable
optionally you would like to override the default (M2_HOME/conf/settings.xml#//
localRepository) where maven creates the local cache repository (default is ~/.m2)
• if necessary add and configure the proxies section in settings.xml. This is a list of
proxies which maven uses to connect to the network like the central mirror.
1.1.3 Eclipse 3.7.x
Download and install any eclipse package version 3.7.x from the eclipse download page here3.
1.1.4 openXMA SDK 5.x
Install the latest openXMA SDK version from the codehaus p2 repository http://
snapshots.dist.codehaus.org/openxma/updatesite/4.
1.2 Developer - Setup
1.2.1 Java 5
Download and install a jdk 5 version from sun5.
1.2.2 Maven 3.x
required for developing and testing openXMA maven plugins (generator,xmapack)
Install maven version 3.0.3 or better from here6 and configure the following environment
variables
• M2_HOME (points to you local maven installation)
• JAVA_HOME (point to your JDK 5 installation)
1
http://java.sun.com/
http://maven.apache.org/download.html
3
http://www.eclipse.org/downloads/
2
4
5
6
http://java.sun.com/
http://maven.apache.org/download.html
1
Installation
• add %M2_HOME%/bin to your PATH environment variable
• optionally you would like to override the default (M2_HOME/conf/settings.xml#//
localRepository) where maven creates the local cache repository (default is ~/.m2)
• if necessary add and configure the proxies section in settings.xml. This is a list of
proxies which maven uses to connect to the network like the central mirror.
1.2.3 Eclipse 3.7
Download and install eclipse classic package version 3.7.1 from here7. (classic package
because the plugin development environment is already preinstalled)
1.2.4 Subversive SVN Team Provider
Install subversive team provider support and connector plugins from the preconfigured
eclipse update site.
1.2.5 Import Team Project Set
Import Team Project Set from http://svn.codehaus.org/openxma/org.openxma.p2-parent/
trunk/projectSet.psf
7
http://www.eclipse.org/downloads/
2
Installation
Figure 1.1. Team Project Set
1.2.6 Workspace Configuration
• switch to 'Working Sets' as Top Level Elements
3
Installation
• set the openXma-indigo target definition as active target platform
4
Installation
• configure classpath variable M2_REPO pointing to your local maven repository folder
5
Installation
6
Installation
• configure the linked resource variable M2_REPO (i.e. your locale maven repository
folder)
Figure 1.3. M2_REPO Linked Resource Path Variable
• resolve the remaining, missing classpath dependencies through executing mvn clean
install on the org.openxma.dsl.platform project
7
Installation
1.3 Project Reference
Every Project in an area has direct dependencies to all other projects in the same area. Every
lower area has dependencies to an higher area, but these are not direct anymore. Therefore an
installed and registered plugin of such a project is needed. You can setup all projects directly
in one workspace and work with dropins (Every needed project eg.: core, dom, pom and
common ... must be exported as plugin to get the generator project compileable), or you can
launch an EclipseApplication inside of Eclipse and export the projects as installed plugins
for the "Inner"-Eclipse.
For more details read the Eclipse Documentation about "Plugin Development" or here8.
1.3.1 Aera 1 - Language
• common - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.common/trunk
Contains common infrastructure code required from more than one of the
other projects. Ideally this project doesn't require any eclipse dependencies (e.g.
resources,jdt).
• core - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.core/trunk
Defines the core model concepts like expressions, a generic type system and other
model elements which are referenced and reused from the openXMA domain and
presentation models. In addition to the (emf)model this project also provides the
usual, eclipse independent Xtext infrastructure services like parser,linking, scoping
and validation.
• core.ui - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.core.ui/trunk
Complemtary to the core project is the core.ui project which defines the necessary
eclipse infrastructure (e.g. editor, outline, content assist) which are required to edit dsl
files described with the core model. In contrast to the core project the core.ui project
has eclipse dependencies.
• dom - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.dom/trunk
The openXMA dom (abbreviation for domain object model) project contains the
domain model part of openXMA (and Xtext components) which are required to
describe domain model concepts like services,entities,dataviews and dataservice
providers.
• dom.edit - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.dom.edit/trunk
Content and label provider classes and other convenience classes that allow EMF
models to be displayed using standard desktop viewers and property sheets.
• dom.ui - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.dom.ui/trunk
Like the core.ui project the dom.ui project also provides the necessary eclipse
infrastructure to work on instances of the dom model.
• pom - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.pom/trunk
The presentation object model or pom project describes the model which references
the core openXMA and domain model and is used to define the presentation layer
part of an application. The pom model also extends the existing guidesigner model
and complements the (graphical) designer model in order to describe some of the
presentation aspects with a better suitable textual dsl .
• pom.ui - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.pom.ui/trunk
8
http://wiki.eclipse.org/Equinox_p2_Getting_Started
8
Installation
Like the dom.ui project the po.ui project also provides the necessary eclipse
infrastructure to work on instances of the dom model.
1.3.2 Area 2 - Generator
• doc - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.doc/trunk
Future documentation project of openXMA with an openXMA welcome page, cheat
sheets and eclipse built in help for openXMA topics.
• feature - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.feature/trunk
For creating update-site
• generator - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.generator/trunk
Workflow,templates and extensions used to generate source code artefacts from
openXMA models.
• generator.mojo
https://svn.codehaus.org/openxma/dsl/
org.openxma.dsl.generator.mojo/trunk
In addition to execute the generator from ant as ant task, the mojo project contains the
maven counterpart to incorporate the generator into an existing maven project build.
• generator.test
https://svn.codehaus.org/openxma/dsl/
org.openxma.dsl.generator.test/trunk
Unit Tests for the generator only
• releng - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.releng/trunk
For creating update-site
• ui - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.ui/trunk
Custom action, views and project creation wizards
1.3.3 Area 3 - Clients
• platform - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.platform/trunk
Contains generic and reusable runtime library code on which generated artefacts are
build upon.
• reference - https://svn.codehaus.org/openxma/dsl/org.openxma.dsl.reference/trunk
Internal reference project to test and verify the openXMA generator and various other
features.
9
Chapter 2: First Example
This chapter describes the necessary steps to create a simple example application supported by the tools
provided with openXMA to model and generate it's persistence, service and ui layer.
This hands-on tutorial will show how to create a new openXMA project, explain its project
structure and finally complete the contained example domain model to generate some code
required for an exemplary 'Search customers' use-case. We will walk you through the
follwing steps
1.
2.
3.
4.
Create a new project
Show how to configure and invoke the openXMA dsl Generator
Start the Application
Complete the initially created domain and presentation model to implement some search
functionality
For this tutorial we will use an example domain and presentation model which are
automatically created by the openXMA project creation wizard. The domain model consists
of one entity named Customer referencing another Address entity in a bi-directional
relationship as shown in the next figure.
Figure 2.1. Demo Model
The presentation model defines ui elements to search,list and edit those entities.
Requirements for this tutorial
• installed maven (know-how) version >= 2.0.10
• installed openXMA plugins
2.1 Create a new project
This sections shows how to create a new openXMA project using the integrated eclipse
➧MDSD supporting facilities. Within this new project we will demonstrate how to create a
domain model and finally generate some code out of it.
1. Start your eclipse with a new workspace (e.g. c:/workspace/test)
2. Create a new project by first opening the File->New->Other dialog from the eclipse main
menu and then selecting the DSL project creation wizard located within the openXMA
category.
10
First Example
Figure 2.2. DSL Project
3. Enter the project name and press the Next button.
11
First Example
Figure 2.3. New openXMA Project
4. Verify and/or adapt the default project and java settings and press the Finish button.
12
First Example
Figure 2.4. openXMA Project Source Settings
13
First Example
Figure 2.5. openXMA Project Libraries Settings
14
First Example
5. At this point its important to validate the completeness of the projects classpath
entries because this is a common source of follow-up errors. Missing classpath entries
are resolvable with the generated 'mvn_dependency_resolve.launch' launch script. For
example, the dsl-platform.jar, amongst other things, provides the default types library
which is required to reuse datatypes like String,Date and so on. If this jar (file) is missing
from the projects classpath the dsl editor will indicate the following error (markers) for
the created model file(s).
6. The openXMA project wizard will create a project with the following structure.
15
First Example
16
First Example
• src/main/java contains all manually written java source code
• src/main/resources contains all manually created resource files like properties,xml,..
• src/main/model contains the openXMA model files
The wizard creates a Customer.dml model file containing the two entities mentioned
above plus one additional service to crud the Customer entity.
• src/test/java contains manually created java test source code
By default the openXMA project wizard will create an example integration test for
the generated Customer service. This test defines a dependency to the CustomerDas
service which doesn't exist until we create it by running the openXMA generator as
shown in the next step.
• src/test/resources contains manually created resources related to testing
Contains one ready-to-run spring configuration file required to run the
CustomerDasTest integration test. This folder also contains launch scripts which we
will use later to run the generator and to start an embedded jetty from maven required
to deploy the openXMA application.
• jetty_run.launch is used to start an embedded jetty server to test the generated
openXMA application
• jetty_stop.launch is used to stop a running jetty server instance
• mvn_dependency_javadoc.launch used download javadocs of the included
dependencies
• mvn_dependency_resolve.launch resolves (downloads) dependencies which are
not already available fromthe local repository
• mvn_dependency_sources.launch used to download source files of the required
dependencies
• mvn_install.launch used to install the project artefact into the local maven
repository
• src/generated/java contains the generated java sources
Initially empty since we haven't started the generator yet.
• src/generated/resources contains generated resource files like properties and xml files
Initially empty.
2.2 Run the Generator
After sucessfully creation of a new project this step gives an overview of how to run and
configure the openXMA generator. Currently there are three existing (and one planned) way
to invoke the generator:
•
•
•
•
as button from the eclipse main toolbar
as menu entry from the resource context menu
outside of eclipse as ant task or maven plugin
future: as integrated eclipse project builder
For the following steps of this tutorial we will use the third option, as maven plugin ,
to invoke the generator with the preconfigured launch script (mvn_install.launch) which
invokes maven with the install goal.
17
First Example
2.2.1 Main toolbar
The first option starts the generator for the currently active domain (a file with dml extension)
or presentation (pml extension) model editor and is available from a button (green triangle
with gear) located on the main toolbar of the eclipse workbench. The generator will only
process those model elements which are defined in the current editor file.
18
First Example
Figure 2.7. Run Generator from main toolbar
19
First Example
2.2.2 Context menu
This option is available from the context menu of a folder selection and runs the generator
for all model files located within the selected folder. Up to now this option is only available
for folders containing domain model files as shown in the next figure.
20
First Example
Figure 2.8. Run generator from context menu
21
First Example
2.2.3 Standalone
For projects which do not want to check-in generated source files it's also possible to run
the generator from an ant or maven build script like it is done in the automatically created
pom.xml of the example project.
<plugin>
<groupId>org.codehaus.openxma</groupId>
<artifactId>dsl-generator-mojo</artifactId>
<version>3.6.2-SNAPSHOT</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate-dom</goal>
<goal>generate-pom</goal>
</goals>
</execution>
</executions>
<configuration>
<componentFileSet>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.pml</include>
</includes>
</componentFileSet>
</configuration>
</plugin>
or as Ant task
<taskdef name="name_you_like" classname="org.openxma.dsl.generator.XmaDslAntTask"
classpathref="classpath.id" />
<!-- Usage with one ui model file: -->
<xmadsl file="C:/myworkspace/myproject/src/org/openxma/dsl/example/simple/Example.
xmadsl" platformUri="C:/myworkspace">
<classpath>
<path refid = "classpath.id" />
</classpath>
</xmadsl>
<!--Usage with a domain model folder (simple project layout): -->
<xmadsl platformUri="C:/myworkspace" domSourceFolder = "src/models" >
<classpath>
<path refid="classpath.id" />
</classpath>
</xmadsl>
Usage and reference for the generator ant task and maven plugin is described more detail in
the Generator reference chapter.
2.3 Start Application
Note: Before we can proceed to this step we assume that the reader has accomplished the
following tasks in the order listed below:
• missing dependencies have been resolved with the preconfigured launch script
mvn_dependency_resolve.launch
• missing src files have been generated with the provided mvn_install.launch script
Upon the above mentioned preconditions have been fullfilled its possible to start and
deploy the openXMA Application with the provided launch scripts. Locate and right click
the jetty_run.launch launch configuration and select Run as -> jetty_run.launch to
start the embedded web server. After the web server has been started and the openXMA
application is deployed it's possible to invoke the example customer component with the
22
First Example
CustomerComponent.launch launch file. Select Run as -> CustomerComponent.launch
to start the CustomerSearchForm which looks like as shown in the next figure.
23
First Example
Figure 2.9. Customer SearchForm
24
First Example
Note: Unless you haven't already configured the XMA_HOME variable you will get the
following error dialog.
Figure 2.10. XMA_HOME
This error dialog means you have to configure XMA_HOME as string substitution variable
as described here1.
1
http://openxma.codehaus.org/docs/XMAGuide/
25
First Example
Figure 2.11. XMA_HOME String Substitution
2.4 Complete domain model
The observant reader has already noticed that the Find button in the search form shown in the
previous step doesn't do anything usefull by default. In this step we will change this default
26
First Example
behaviour and complete the search functionality to actually invoke some service method to
actually retrieve search results from a database finder. To implement this new functionality
will we will change the respective model elements in the following 3 steps
• create a new query operation in the CustomerDao repository
• delegate to this query operation from within the customer dataaccess service
(CustomerDas)
• adapt the search form to invoke the finder and show the results in a table control
• restart the application to verify the new functionality
2.4.1 Create a new CustomerDao query operation
For this step it's first of all necessary to locate and open the Customer entity. You can either
to this conventionally by searching the domain model file containing the Customer entity or
by using the integrated 'Open Xtext Element' dialog by pressing Ctrl+Shift+F3 which gives
you the following dialog window.
27
First Example
Figure 2.12. Open Xtext Element
Type in 'CustomerDao' as element name and hit enter to open and edit the dml file where
the customer entity is contained in. To implement the new search functionality first add a
operation declaration to the CustomerDao as outlined below.
repository CustomerDao for Customer {
operation Customer[] findAllLikeName(firstName,lastName) :
from Customer as customer
where
customer.firstName like :firstName and
customer.lastName like :lastName
}
28
First Example
2.4.2 Adapt CustomerDas Service
In order to make this database finder accessible from the customer service, which is invoked
from the customer search page, we need to include and reference it in the CustomerDas
service located within the same dml file. Locate the service declaration and add the following
line as outlined in the next listing.
service CustomerDas {
Customer.crud
CustomerDao.findAllLikeName
}
With the previous two additions in place we have to invoke the generator again in order to
create the new service and dataaccess methods we need to integrate in the next step. Thus
mouse over and press the openXMA generator button in the main menu toolbar as explained
here: Run Generator from main toolbar
2.4.3 Invoke the query operation
Next we will change the provided CustomerSearchForm page to actually invoke the finder
operation on the generated CustomerDas service whenever the findCustomer event is
triggered. Therefore we must add the following lines to the findCustomer method lcated
within the CustomerSearchForm page class.
public void findCustomer(RemoteCall call, RemoteReply reply) {
getTypedComponent().customers = getTypedComponent().customerDas.
findAllLikeName(customer_firstName.getEncodedValue(),customer_lastName.
getEncodedValue());
customerTableFill();
}
2.4.4 Restart the Application
In this last step we will restart the application to verify the results from the previous
changes. If you haven't done so far first stop the embedded server and CustomerComponent
launched in the previous steps. Right click the jetty_stop.lauch script and select Run as ->
jetty_stop.launch to stop the application. Next restart the server and CustomerSearchForm
component as described here Start Application and try to search for some customer by
entering values for first- and lastname.
29
First Example
Figure 2.13. CustomerSearchForm with database search results
30
Chapter 3: Overview
This chapter gives an overview of the design objectives, approaches and patterns incorporated by
openXMA to support the application of model driven software developement techniques.
3.1 General
First of all the approach of openXMA is model driven which means that the presentation
layer is described/designed in a model from which code is generated. Finally custom code
can be added manually.
Figure 3.1. Model driven software development
All steps are done by an integrated open source tool chain whithin Eclipse. The generated
and also the custom code consists of Java classes and resource files.
These coexistence of generated and manually code is a basic princip of openXMA. This
allows to utilize the power of model driven development and still have the flexibility of
manual coding.
3.2 Technology
The following figure outlines the typical development stages and shows the various
technologies used per stage:
31
Overview
32
Overview
1. in the initial design stage various frameworks (EMF,TMF,GMF,M2T,EMFT ) from the
eclipse modeling project are used to work with models which are used to describe and
generate certain aspects of an application
2. within the build stage both ant and maven are supported to process, generate and build
applications using those models
3. finally the generated code builts upon the openXMA platform which contains generic and
parameterizable code to reduce the amount of generated code. The openXMA platform
in turn uses spring and hibernate. Spring is used for dependency injection and declarative
transaction handling and hibernate for persistence.
3.3 Modular (textual) models
One fundamental design goal of the openXMA Dsl is the support of modular models i.e. to
split and group model elements into several distinct files with the possibility to reference
model elements located in other model files. The advantages of this modular approach are
• team development friendly (think work-sharing)
• textual,dsl based models work out of the box with existing VCS (merge,diff)
• scalability issues with so called 'whole World' models where every information you
want to capture is put into one single model
• typical advantages gained by applying modular concepts such as reusability, higher
cohesion, separation of concerns, single responsibility principle,
The mechanism used to reference model elements located within other files is based on the
already-known and established classpath concept. This way it's possible to use so called
classpath URI's pointing to other models which uniformly works from within an eclipse
project or outside as standalone java application. The way to reference model elements
located in different model files is done with the import keyword as shown in the following
example created by the openXMA project creation wizard. The format of the import value is
a so called Uniform Resource Identifier (URI)1 and essentially defines where to find a given
file.
import
"classpath:/org/openxma/demo/customer/xma/CustomerComponent.xma" import
"classpath:/org/openxma/demo/customer/Customer.dml" component
xma::CustomerComponent uses CustomerDas { data Customer customer data
Customer[] customers }
The previous example shows an openXMA dsl component which contains references to the
following external model elements:
• CustomerComponent which is only referable trough the import statement to the
CustomerComponent.xma file
• Customer and CustomerDas which are located in the Customer.dml file
The usage of a classpath based mechanism to refer and import external model elements
implies the fact that these model files have to be made available on the projects classpath.
This can be achieved by storing the model files side-by-side with the projects java src files
or by creating a separate src folder as it is shown in the next figure.
1
http://download.eclipse.org/modeling/emf/emf/javadoc/2.5.0/org/eclipse/emf/common/util/URI.html
33
Overview
An exception to this rule which doesn't require an explicit import declaration are references
to simple types available with the openXMA dsl standard type library. This library file comes
34
Overview
with a set of several preconfigured types which are commonly required and therefore reusable
accross projects. This model is shipped togehter with the openXMA platform library and
contains common datatypes and validators for String,Integer,Date,etc. Altough this types are
automatically imported it is also required to add this platform library to the projects classpath.
3.4 Layered models
Another design objectives was to support the creation of enterprise applications which are
composed of layers build on one another. This support should incorporate the individuall
usage of each distinct model counterpart without the need to always need the complete
modeling stack (e.g. it is possible to apply the domain and presentation models independently
from each another). This division into several distinct model types also mirrors the layered
architecture of the stereotypical kind of application we wanted to target with this ➧MDSD
approach.
Figure 3.2. Layered Archictecture
2
[1]
Hence in order to independently support the typical layers of an application we came up with
the following different model types.
2
Source: Evans, 2003, S. 68.
35
Overview
Figure 3.3. Layered Models
• A type model which contains common basic types and their associated validators
(models with a .xmadsl file extension) togehter with a platform library for resuable
generic code
• A domain model to represent the domain objects of an applications such as
Service,Entity,ValueObjects and Views (model with a .dml file extension)
The default supported technology stack of this model type is a combination of the
spring3 and hibernate4 frameworks.
• A presentation model for ui related components capable to bind model elements from
the domain layer such as entities and views. (model with a .pml file extension).
The default supported technology stack of this model type is the openXMA5
framework.
3.5 Integrated models
By integrated we mean the following model qualities
• different model types should be integratable with each other
3
http://www.springsource.org/about
http://www.hibernate.org/
5
http://openxma.codehaus.org/
4
36
Overview
• support alternative ways to work with models to best support the task at hand
• integration with existing developer tools
The following figure gives an example of three different ways to visualize and edit models
which all are based on a common textual source definition.
37
Overview
Figure 3.4. Integrated models
38
Chapter 4: Presentation Layer
Modeling
This chapter describes the approach of openXMA for modeling and creating the presentation layer.
4.1 Approach of modeling the presentation layer
For developing the presentation layer different aspects are relevant. According to these
aspects several types of models are used. Additionally models which are not part of the
presentation layer itself provides presentation information and are therefore referenced from
the presentation model.
Figure 4.1. Models containing information about the presentation layer
4.1.1 Presentation model
This is a platform independent model of the user interface. It contains the content and its
structure as well as the data binding and the event mapping.
For editing of this model a textual editor is used.
The presentation model and tooling is described in detail in chapter 1.2
4.1.2 Design model
This is a detailed and platformdependent model (SWT) of the user interface. It typically
contains the gui elements and their layout.
For viewing and editing this model a graphical WYSIWYG editor is used. The model and
the according tooling is described here: http://openxma.codehaus.org/docs/XMAGuide/
39
Presentation Layer Modeling
4.1.3 Referencing the domain model
The most importent model für generating code for the presentation layer is of course the
presentation model itself. But there is also information in the domain model which is relevant
for presentation. The data binding for example references attributes of entities and views
which are defined in the domain model. The approach of openXMA is to define metadata
(formatter, constraints,...) on attributes and types in the domain model and use these metadata
in the presentation model(s) for presentation purposes.
Example: A maximum lenght constraint for a attribute is used for the validator to limit the
amount of characters which can be entered in a text field.
4.2 Presentation model introduction
This chapter describes and explaines by examples of what the presentation model consists.
Figure 4.2. UI definition model and editor
4.2.1 Structuring the presentation layer
There is not one presentation model for a whole application. The presentation layer of
an application is split into modules. The modules themself are grouped hierarchically into
packages. According to this the presentation model is devided into models per components
which are contained in packages. So for each component exists a component presentation
model and a component design model.
40
Presentation Layer Modeling
Figure 4.3. Component models
Components
Pages
A page is something like a dialog, a form which is shown on the user interface and which
contains some controls. Pages can be defined within a component. It is intended that a
component contains few closely related pages or just one page.
4.2.2 Hello world example
In this example we create a component which contains a very simple page.
Create a component
The first step is to create a new component. The component model files are created by a
Wizzard. The following example shows how to do this:
The first step is to create a new component. The component model files are created by a
Wizzard. The following example shows how to do this:
Figure 4.4. Select component creation wizzard
41
Presentation Layer Modeling
Figure 4.5. Define the package and component name
Finishing the wizzard creates the models of an empty component:
Figure 4.6. Component model files
Figure 4.7. Presentation model for empty Component
In this texual model the first line imports the design model of the component in order to allow
referencing manuel designed elements of the component by the textual presentation model.
The presentation model has to define or reference exactly one component from the design
model. The according keyword is 'component'. In this example a component with the name
'MyComponent' is defined in the design model and referenced by the textuel model. All
references to the design model does have the prefix 'xma::'. Defining the component in the
design model allows us on the one hand to add manuel designed Pages to the component in
the design model and on the other hand to add a plattform independent model of a Page in
the texuel presentation model. This is described next.
Create a page
In the next step we create a minimal empty page within the component. This is how it is done:
Figure 4.8. Component with empty page
42
Presentation Layer Modeling
In the next example the size of the page is defined (keywords are 'width' and 'height') and a
very simple content is modeled. The content is a label with the name 'helloLabel' shows the
string 'Hello World' and a button with the name 'closeButton' which shows the string 'Close'.
Figure 4.9. Component with hello world page
In this example no explicit layout is defined therefore the default layout strategy is applied.
This means that the label (as any data control) is attached on the left side of the page and the
button (as any buttons) attached on the right side of the page.
From this texual presentation model a graphical design model is generated by pressing the
generate button in the tool bar. In this example the design model is read only because the
whole design model is generated out of the textual presentation model (by applying the
default layout strategy) and therefore will be overwritten by the next generation of this
component. This is how it looks like:
Figure 4.10. Design model of hello world page
4.2.3 How to model a page
A page model consists of the following model elements:
1.
2.
3.
4.
5.
6.
Data objects
Content
Commands
Menus
Event mapping
Conditions
To explain the elements of a page we create a page which allows to edit a customer.
Data objects
For the databinding data objects are used. Data objects are defined instances of entities or
other complex types of the domain model. Therefore a data type (e.g. a entity) is referenced
and a name is defined for the instance.
43
Presentation Layer Modeling
In this example there exists an entity with the name "Customer" in the domain model. The
following steps have to be done to define a data object for a entity:
1. Import the model file in which the entity "Customer" is defined
2. Create a reference to this entity.
3. Define a name for the data object.
The next figure shows how this looks like:
Figure 4.11. Customer data objects
Content
First we add 2 panels to the content the first one is a vertical panel in which we want to show
the fields of the customer and the second one is a horizontal panel for some buttons. Because
we want to show the buttons on the right side of the page this panel is aligned right.
To show fields for the attributes of the customer the data object is referenced (by name) then
(behind a dot ".") the attribute is referenced (also by name). These references can be followed
in the Editor by pressing "F3" or "Ctrl"+ the right mouse button.
Additional the 'Ok' and 'Cancel' buttons are inserted in the second panel.
Figure 4.12. Content with customer attributes
The next step is to generate the design model which then can be opening with the graphical
guidesigner. This is shown in the next figure.
44
Presentation Layer Modeling
Figure 4.13. Content preview
Commands
Commands of a page are for example used to initiate navigation or to call service operations.
There are 2 different types of commands:
1. A client command is executed on the client side. No server call is made.
2. A server command is executed on the server side and invoked by a remote call from a
according client command.
In this example we create a command to load the data for the customer. The implementation
of this command will invoke services on the server. Therefore it is a server command.
Furthermore we create a command to save the data which also invokes a service on the server.
Finally a client command to close the page is created. This can be done on the client side.
Figure 4.14. Commands
Built-in Commands
Because of they are needed in nearly every page, there are two built-in commands to close
the page:
• closeOK
• closeCancel
These two commands have exactly the same effect except the result value passed to the caller:
closeOK returns true, closeCancel false.
Navigation Commands
Another repeating command assignment is navigation, e.g. loading other pages or
components. These cases can be modelled as well. Be aware, that navigation happens always
on the client side, what means, we are dealing here with uicommands. So a simple example
is to call an other page as seperate, usually modal, window:
Figure 4.15. Simple Page Invocation
In the same manner a modelled component can be invoked. Further every XMA component
can be invoked when defining a Proxy with the URL of the component:
45
Presentation Layer Modeling
Figure 4.16. Proxy Definition and Invocation
Proxies are by the way not part of a component or page, so they has to be defined outside.
Embedding Pages and Components
Instead of launching a page or a component each of them can be enbedded into a panel of
the caller. In that case the page (or main page of the referenced component) becomes visual
and in behavior part of the caller (parent-) page. It stays there as long as no other page or
component will be invvoked in the same panel:
Figure 4.17. Embedding
As obvious, the uicommand must be a page command and the referenced panel has to be in
the content section of the same page.
Passing and returning values
Often it is necessary to pass additional information to the invoked page/component or to get
back some kind of result. This is supported by invocation property mapping:
Figure 4.18. Embedding
On the left side are the elements of the caller (page or component), on the right side are
properties (only they are part of the public interface!) of the invoked page or component. If
there is more than one mapping, the mappings has to be seperated by a comma. On the left
side can be used
• Members of data objects, if they are used on the page as ui element (only then they
exist on client side)
• Widgets (like "text")
• Page - or Component properties
The Symbol in the middle of the mapping defines the direction (and the moment) of the data
passing:
46
Presentation Layer Modeling
• -> (IN) passing from caller to invokeable before invoking
• <- (OUT) passing from invokeable back to caller after invoking
• <-> (INOUT) passing two times in two directions (combination of IN and OUT)
Be aware, that OUT and INOUT are only possible on non-embedded modal pages (modality
is a property on the page). Then the result deliveries will be done after the foreign page was
closed with closeOK() (on closeCancel no results will be passed back).
Menus
Three types of menus are supported:
• dropdownmenu: The usual menu on top of the window. Images mnemonics and
accelerators are supported. A page can have only one drop-down menu.
• treemenu: The menu will be displayed as a Tree Widget into a given panel. No support
for images or mnemonics or accelerators. A page can have several tree menus, but
each must be displayed in an other panel.
• tabmenu: The menu will be displayed as (possible nested) Tab Widgets into a given
panel. Support for images and mnemonics, but not for accelerators. A page can have
several tab menus, but each must be displayed in an other panel.
The content of a such menu structures is always the same: one or more menu items containing
possibly one ore more menu items. Features which are not supported for the current menu
type (like images in tree menus) will be ignored.
Figure 4.19. Menus
Menuitems
A menu item has at least an unique model name and a text to display. It can contain other
menu items. Further, it can have
• A mnemonic (not in tree menus): Using the character '&' in the text marks the
following character as mnemonic.
47
Presentation Layer Modeling
• An accelerator (only in drop-down menus): An application-wide key combination to
call the menu e.g. it's related action
• An image (not in tree menus): A gif, png org bmp file referenced by it's location at
runtime in the classpath.
Menus can be mapped by their model name to commands in the event mapping section.
Event mapping
To define when the commands are invoked a mapping of events to commands is needed. The
event mapping is defined in a seperate section in a page. For the mapping of an event the
following information is needed:
1. Name of the control or menu item which fires the event. For events of the page itself no
control needs to be specified.
2. Name of the event or menu item. If no event is specified the default event for a control
is used.
3. Command which is invoked by the event
To load the data we map the init event of the page to the loadData command. To save the data
the ok button is mapped to the saveData command and to close the page the cancel button
is mapped to the cancel command:
Figure 4.20. Event mapping
Two or more commands can be concatinated, seperated by comma, for one mapping entry.
Conditions
A condition of a page describes a dedicated aspect of the state of the page. In difference to
a state a page can have more than one condition at the same time. To define a condition the
following information is needed:
1. Name of the condition
2. Boolean expression.
of a page the conditions are calculated and can be used for the grey logic (= e.g. enabling or
hiding fields). The intended use of conditions is: Describe all required conditions in a section
of the page and use them to set the greylogic properties of gui elements. This should result
in a better overview of the logic conditions of a page and in reducing redundancies in the
implemention of the grey logic.
In the example we want to disable the Ok-Button in case some constraints of gui elements
of a page are violated. This looks like this:
48
Presentation Layer Modeling
Figure 4.21. Conditions and button enabling
Logic blocks
Logic blocs can be used to centralize GUI logic upon conditions that were described in the
previous chapter.
In a switch-like structure attributes of elements can be set upon wheter a condition
evaluates to true. Before the first case statement, the default state of elements can be set:
Figure 4.22. Logic blocks, conditions and widget visibility
4.3 Formatters and Validators
Formatters and validators are used to convert primitive XMA types to the UI and back.
49
Presentation Layer Modeling
4.3.1 Referencing formatters in PML
The concept of formatters/validators is part of the underlying Design Model but can be easily
referenced in DML attributes . The behaviour is then adopted to the GUI elements as
described in the following section.
4.3.2 Concept
Subclasses of IFmt1 are responsible for converting primitive XMA types (see IAtomic2 )
to the UI and back. The former operation is usually called formatting , the latter validation
. Both operations are encapsulated in an instance of a class derived from IFmt3. The next
figure show the involved data-flow:
Figure 4.23. formatting/verifying data flow
Every type defines a so called internal encoding , that's how the value is stored in widgetmodels and also transmitted over the wide area network between XMA client and XMA
server. Every formatter is able to map the internal encoding to an external encoding , that's
the string-representation how the value is displayed in the SWT-widget to the end-user.
4.3.3 Validation
The next interaction-diagram shows an event-flow to demonstrate how validation works in
XMA:
Figure 4.24. validation event flow
On every keystroke, two operations are performed:
1. Verification: For every new characters inserted in the UI-text-field, the methods
isLegalExternalChar() and maxLenOfExternal() are consulted to decide if
1
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/IFmt.html
http://openxma.codehaus.org/docs/api/xma_runtime/at/spardat/xma/mdl/IAtomic.html
3
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/IFmt.html
2
50
Presentation Layer Modeling
the new characters may even be input and if the maximum length would not be exceeded.
If at least one of these checks fails, the modification is rejected in the UI-text-field.
2. Modification: If the input passes the verification-step, the input is accepted and the UItext-field will show the modification. However, the input may not be valid. The XMARuntime calls the method IFmt.parse to convert the external string-encoding to an
internal. parse throws an AParseException4 if the input is rejected. The message-text
of the AParseException will be shown in the status-line of the dialog that contains the
erroneous field.
4.3.4 Formatting
Formatting is the process of mapping an internal string-encoding of a supported XMA type
to an UI-encoding adequate for the end-user, see the next interaction diagram:
Figure 4.25. formatting event flow
Method IFmt.format does all the work.
4.3.5 Custom formatters
Custom formatters are created by using the class FmtFactory5 and providing a stringpattern. See FmtFactory 's javadoc for a list of permissible string-patterns. The create
-methods of class FmtFactory require a string-pattern and return an object derived from
IFmt6 , which is the base class for all custom formatters.
If performance is of great importance in your application, do not use FmtFactory .
Instead, create the formatters directly by using the getInstance() -methods in the classes
ABcdFmt7 , ADateFmt8 , AStringFmt9 and ATimeStampFmt10 .
The following table gives some examples of formatter-patterns, although this list is just an
example. For a complete description please refer to FmtFactory11 .
pattern
meaning
s,35
String which must not be longer than 35 digits.
s,35,m
As above, but mandatory (expressed by style "m"), i.e., must not be empty.
sm,3,5,m
At least 3 and at most 5 characters must be entered.
4
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/AParseException.html
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/FmtFactory.html
6
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/IFmt.html
7
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/ABcdFmt.html
8
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/ADateFmt.html
9
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/AStringFmt.html
10
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/ATimeStampFmt.html
11
http://openxma.codehaus.org/docs/api/epclient/at/spardat/enterprise/fmt/FmtFactory.html
5
51
Presentation Layer Modeling
sr,0,35,a-c
String of length not more than 35 consisting only of characters { 'a', 'b', 'c' }.
n,13,2,m&t
Numeric with at most 13 digits before and 2 after the dec-point, example:
-1.000.000,99 in the austrian locale
n,inf,2,nn&m
Numeric; example: 10000000000000,99 in the austrian locale. Negative inputs are
not allowed (expressed by style "nn"). The number of digits in front of the decimal
separator is not limited.
nr,inf,0,5,596
Integer number between 5 and 596.
nr,13,0,inf,596
Integer number between -9999999999999 and 596.
d
Date using the format 24.04.2000 (this is Locale-dependent).
d,m
As above, but input is required.
d,m&ful
Mandatory date using the format "Montag, 24. April 2000" (expressed through style
"ful"), although this is locale-dependent.
dr,0,7,m
Mandatory date that must lie in the range [today ... today plus 7 days].
dp,dd-MM-yyyy,m
Mandatory, non-localized date format as defined in
java.text.SimpleDateFormat, e.g., "24-04-2000".
Table 4.1. FmtFactory patterns
4.3.6 Writing own custom formatters
XMA
provides
a
built-in
set
of
formatters
in
the
package
at.spardat.enterprise.fmt . Writing your own formatters is usually done by
subclassing one of the provided formatter-classes. Creating your own formatter-classes is
recommended if you can reuse the formatter for many text-fields, so that the formatting/
validation-code can be shared. Suppose, numeric input should be constrained for a particular
field in a way, that only integer numbers x having (x%1000==0) with a maximum length
of 10 digits are allowed. Then your subclass may look like that:
public class MyNumberFmt extends ABcdFmtDefault {
/**
* Constructor
*/
public MyNumberFmt (int style, Locale l) {
super (10, 0, style, l);
}
/**
* @see at.spardat.enterprise.fmt.IFmt#parse(String)
*/
public String parse (String external) throws AParseException {
/**
* Call all the validation-logic of the super-class. This call would throw
an exception if something
* would went wrong.
*/
String internal = super.parse (external);
/**
* Create an Atom from the internal string
*/
Atom
atom = Atom.newInstance (Types.T_BCD, internal);
if (atom.hasValue()) {
long
val = (long) atom.toDouble();
if ((val%1000) != 0) throw new AParseException ("Please enter a number
that divides by 1000.");
}
return internal;
}
}
Conclusion: Narrowing the validation rules is accomplished by extending method parse ,
but do not forget to call parse on the superclass.
52
Presentation Layer Modeling
4.4 Component
To model a component in openXMA dsl the component has to be defined in openXMA core.
The content and details of this component ccan be defined in openXMA dsl.
4.4.1 Modeling a component
component xma::name
[uses= service1,service2,...] { data objects... commands... eventmapping...
conditions... pages... variable definitions... }
Allows to define the content of a component which has been defined in openXMA core. n
and graphical design within the same page.
• name: The name of an existing (not generated) component in the design model of
openXMA core.
Example:
/**
* Description of this component.
* The JavaDoc style comments of PML elements will be adopted in the
* generated Java code.
*/
component xma::CustomerComponent uses CustomerDas {
... }
Modeltransformation:
For openXMA core no new component is created but the content of an existing openXMA
core component can be extended.
4.4.2 Data object variables
data type [ ] name
Models a data object variable.
• type: Type of the data object variable. The type is defined by referencing a complex
type (Entity, DataView or Struct).
• square brackets: To define a collection instead of a single object append a opening
and a closing square bracket.
• name: The name of the data object variable.
Example:
data Customer customer data CustomerView[ ] searchResult
Modeltransformation:
For openXMA core there is a XMAText created.
4.4.3 Commands
( command | uicommand ) name
Models a command. There are server-side commands (keyword=command) and client-side
commands (keyword=uicommand).
• name: The name of the command.
53
Presentation Layer Modeling
Example:
command loadData uicommand closePage
Modeltransformation:
For openXMA core there is a XMAFunction created.
4.4.4 Event mapping
eventmapping {
[onInit -> referenceToCommand1] referenceToElementWithEvent1[.eventType] ->
referenceToCommand2 ... }
Defines the mapping of events to commands.
• referenceToCommand: Reference to a command
• referenceToElementWithEvent: Reference to a element with events.
Example:
eventmapping {
onInit -> loadData
cancelButton -> closePage
detailTable.onDefaultSelection -> showDetails }
Modeltransformation:
For openXMA core there are references to modeled comands generated to the elements which
have events.
4.4.5 Conditions
conditions { nameOfCondition1 = booleanExpression1 ... }
Defines conditions. Conditions are intendes to be assigned to the values of flags like visible,
enabled, mandatory.
• nameOfCondition1: The name of the first condition
• booleanExpression1: The boolean expression which defines (or describes) ther
condition.
Example:
conditions {
addressAcceptable = zip.hasValue() && city.hasValue() && street.hasValue()
searchResultAvailable = resultTable.size()>0
readyToSave = CustomerEditPage.isValid()
}
Modeltransformation:
For openXMA core a XMAStateFlage is generated.
4.4.6 Definitions
definitions {
string name1 = value1
attachmentposition name2 = value2
... }
Definition of variables of type string or of type attachmentposition.
• names: The name of the variable
54
Presentation Layer Modeling
• values: The values of the variable.
Example:
definitions {
string str1 = "Address"
attachmentposition pos1 = 50%+3 }
Modeltransformation:
There is nothing generated for openXMA core. The variables are used only within the textual
presentation model.
4.4.7 Details for Core/SWT applications
For more details about components in the underlying core/SWT part, see Section 4.1,
“Component” in XMA Guide .
4.5 Pages
4.5.1 Page
page name "text" [statusBar=true|false] [center=true|false]
[uses= service1,service2,...] {
page properties...
data objects...
data mappings...
menus...
commands...
eventmapping...
conditions...
logic block...
content variable definitions
definitions...
}
Displays a page.
•
•
•
•
name: The name of the page.
text: Label of the page.
statusBar: defines if the page should have a status bar or not.
center: defines if the page should be centered relative to it's parent or, if non, to the
screen.
TODO: Comment also the props height, width, style, modality, image, titlebuttons and
resizeable,
Example:
/**
* Description of this page
*/
page EditCustomerPage "Edit customer data" uses CustomerDas {
... }
Modeltransformation:
For openXMA core there is a DialogPage created.
4.5.2 Referenced page of openXMA core
page xma::name
55
Presentation Layer Modeling
[uses= service1,service2,...] { data objects... commands... eventmapping...
conditions... content variable definitins... }
Displays a page which has been defined in openXMA core. The page itself (size,title,...) is
designed in openXMA core but the content or parts of its content is defined in openXMA
dsl. This is used to combine textual design and graphical design within the same page.
• name: The name of an existing (not generated) page in the design model of openXMA
core.
Example:
page xma::EditCustomerPage uses CustomerDas {
... }
Modeltransformation:
For openXMA core no new page is created but the content of an existing openXMA core
page can be extended.
4.5.3 Details for Core/SWT applications
For more details about components in the underlying core/SWT part, see Section 4.3, “Page”
in XMA Guide .
4.6 Simple GUI elements
4.6.1 Label
label [name] "text"
[align=left|right]
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
Displays a static text.
• name: The name of the label.
• text: String which represents the static text.
Example:
label firstNameLabel "First name:"
Modeltransformation:
For openXMA core there is a XMALabel created.
4.6.2 Text
text name
[align=left|right]
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
[format=formatdefinition]
Displays a text box.
• name: The name of the text box.
56
Presentation Layer Modeling
Example:
text firstName
Modeltransformation:
For openXMA core there is a XMAText created.
4.6.3 Combo
combo name
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
[format=formatdefinition]
Displays a combo box.
• name: The name of the combo box.
Example:
combo gender
Modeltransformation:
For openXMA core on default a SimpleCombo created. In case there is a domain-formatter
defined a XMACombo is created.
4.6.4 Checkbox
checkbox name "text"
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
Displays a checkbox with a label.
• name: The name of the checkbox.
• text: String which is the displayed label of the checkbox.
Example:
checkbox maritalState "Is married"
Modeltransformation:
For openXMA core there is a CheckButton created.
4.6.5 Radiobutton
radiobutton name "text"
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
Displays a radiobutton with a label.
• name: The name of the radiobutton.
• text: String which is the displayed label of the radiobutton.
57
Presentation Layer Modeling
Example:
radiobutton redColor "Red"
Modeltransformation:
For openXMA core there is a RadioButton created.
4.6.6 Seperator
seperator name
[orientation=horizontal|vertical]
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomtattachment]
Displays a horizontal or vertical line.
• name: The name of the seperator.
Example:
seperator sep1
Modeltransformation:
For openXMA core there is a XMALabel created where the flag SWT.SEPERATOR is set.
4.6.7 Tree
tree name
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomtattachment]
Displays a horizontal or vertical line.
• name: The name of the tree.
Example:
tree tree1
Modeltransformation:
For openXMA core there is a XMATree created.
4.7 Panels
Panels are used to structure the content of pages. All kind of panels can be nested in each
other.
4.7.1 Panel
panel [name] ["text"]
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
[scrollable=scrollableflag]
[tabulatordefinition] { child gui elements... }
58
Presentation Layer Modeling
Displays a panel in which the nested elements can be layouted free using the formlayout. On
default the nested elements are attached left. The first element is attachted top to the parent
and each following element is attached at the bottom of the previous element.
• name: The name of the panel.
• text: String which is the displayed label of the panel.
Example:
panel {
... }
Modeltransformation:
In case the label text is not defined for openXMA core a XMAComposite is created and when
the label text is defined a Group is created.
4.7.2 Vertical panel
vpanel [name] ["text"]
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
[scrollable=scrollableflag]
[tabulatordefinition] [margin=marginsize] [spacing=spacingsize] { child gui
elements... }
Displays a panel which content is structured in vertical cells. On default each nested elements
is put in a cell and can be just layouted within this cell using the formlayout.
• name: The name of the panel.
• text: String which is the displayed label of the panel.
Example:
vpanel {
... }
Modeltransformation:
In case the label text is not defined for openXMA core a XMAComposite is created and
when the label text is defined a Group is created. Additionally for each cell of the panel a
XMAComposite is created.
4.7.3 Horizontal panel
hpanel [name] ["text"]
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
[scrollable=scrollableflag]
[tabulatordefinition] [margin=marginsize] [spacing=spacingsize] { child gui
elements... }
Displays a panel which content is structured in horizontal cells. On default each nested
elements is put in a cell and can be just layouted within this cell using the formlayout.
• name: The name of the panel.
• text: String which is the displayed label of the panel.
Example:
59
Presentation Layer Modeling
hpanel {
... }
Modeltransformation:
In case the label text is not defined for openXMA core a XMAComposite is created and
when the label text is defined a Group is created. Additionally for each cell of the panel a
XMAComposite is created.
4.8 Field
refereceToDataobject . referenceToAttribute
[align=left|right]
[width=heightdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomtattachment]
[format=formatdefinition]
Displays controls for a attribute of a dataobject. These are a label, a data control
(text,combo,checkbox ordatapicker) and a label which displays the unit of the attribute (e.g.
%, mm,kg,...).
• name: The name of the text box.
• referenceToDataobject: Reference to a defined data object of the page or the
component.
• referenceToAttribute: Reference to a attribute of the referenced data object.
Example:
customer.firstName
Modeltransformation:
For openXMA core there is a Label for the label text a data control and a label for the unit
created.
4.9 Table
4.9.1 Table body
table name key=refereceToDataobject.referenceToAttribute
[width=widthdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
[columnMinWidth=minwidthdefinition] { tablecolums... }
Displays a table with column and rows.
• name: The name of the table.
• referenceToDataobject: Reference to a defined data object of the page or the
component.
• referenceToAttribute: Reference to a attribute of the referenced data object.
Example:
table searchResultTable key=customer.oid {
... }
60
Presentation Layer Modeling
Modeltransformation:
For openXMA core there is a XMATable created.
4.9.2 Table column
[refereceToDataobject.]referenceToAttribute
[visible=columnvisibility]
[label=columnlabel]
[align=columnalignment]
[width=columnwidth]
[minWidth=columnminwidth]
[maxWidth=columnmaxwidth]
Displays a table column.
• referenceToDataobject: Reference to a defined data object of the page or the
component.
• referenceToAttribute: Reference to a attribute of the referenced data object.
Example:
firstName
Modeltransformation:
For openXMA core there is a XMATableColumn created.
4.9.3 Table combo
tablecombo name key=refereceToDataobject.referenceToAttribute
[width=widthdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment]
[columnMinWidth=minwidthdefinition] { tablecolums... }
Displays a combobox with column and rows. It is behaves like a read-only table that allows
for selection of one table row.
The tablecombo widget is realized with the Nebula TableCombo widget which is not included
in the standard SWT library. If you want to use this widget you have to add the library to
your client platform:
• Install XMA TableCombo Add-on from the install Tool and add it to classpath of
your project.
• Maven: Add the client library tablecombo=clientrt to the xmapack plugin
configuration
• Ant: Copy this library to the clientrt and seal it with: <copy
file="${tablecombo_jar}"
preservelastmodified="true"
tofile="${webappDir}/clientrt/tablecombo.jar"/>
<xmachecksum
file="${webappDir}/clientrt/
tablecombo.jar"/>
• Open xma-app.xml and add the library as resource: <resource
href="clientrt/tablecombo.jar" type="jar" version="" />
• In
the
project's
root.properties,
add
this
configuration:
xma.runtime.UIDelegateFactoryClient=org.openxma.addons.ui.tablecombo.mdl.NebulaAwareUIDele
Modeltransformation:
For openXMA core there is a XMATable created.
61
Presentation Layer Modeling
4.10 Tab folder and tab pages
4.10.1 Tab folder
tabfolder name
[width=widthdefinition] [height=widthdefinition]
[left=leftattchment] [top=topattachment] [right=rightattachment]
[bottom=bottomattachment] { tabpages... }
Displays a tab folder.
• name: The name of the tabfolder.
Example:
tabfolder dataTabFolder {
... }
Modeltransformation:
For openXMA core there is a Notebook created.
4.10.2 Tab page
tabpage name "text" { child elements... }
Displays a tab page of a tab folder.
• name: The name of the tabpage.
• text: Label of the tabpage.
Example:
tabpage BasicDataPage "Basic data" {
... }
Modeltransformation:
For openXMA core there is a NotebookPage created.
4.11 Layout
4.11.1 Tabulator
tabulator = [name:][ percentOfParent% ] [ (+|-)offset] [, ...]
Defines vertical help lines to which gui elements can be attached. Tabulators can be defined
for panels.
• name: Name of the tabulatorposition.
• percentOfParent: Percent of the space of the parent.
• offset: Absolute offset.
Example:
tabulator=100,tabX:300,100%-10
Modeltransformation:
62
Presentation Layer Modeling
For openXMA core there is nothing own created but the positions defined by the tabulator
are used to define the attachment of gui elements.
4.11.2 Attachment
sideToAttach=
[percentOfParent% | (siblingGuiElement | relativeSibling ) [.sideOfSibling] |
tabulatorposition]
[(+|-)offset]
Defines attachments of a gui element.
• sideToAttach: The side of this gui element for which the attachment is defined. This
can be: left,top,right,bottom,vcenter
• percentOfParent: Percent of the dimension (width or height) of the parent.
• siblingOfGuiElement: The name of a sibling of this gui element.
• sideOfSibling: The side of the sibling. This can be: left,top,right,bottom,vcenter.
• tabulatorposition: The reference to a tabulatorposition either by
tab(indexOfTabulatorposition) or by the name of the tabulatorposition
• offset: The offset to the parent or the specified position.
Example:
left=0
left=firstName.left
top=previous
right=50%-10
right=okButton-5
right=next-5
right=tab(2)
right=tabX
bottom=100%
Modeltransformation:
For openXMA core there is a XMAFormAttachment created.
4.12 Styles
Styles can be used to enrich the presentation model generically. Styles define properties
which are set to elements of the presentation model for which a style should be applied. To
define on which model elements a style will be applied one can define a selector for a style.
The concept of styles in openXMA is similar to styles in CSS but there are some differences.
Example:
style MyStyle select label {
background-color: yellow
foreground-color: red
}
In this example a style named 'MyStyle' is defined. This style defines the background color
and the foreground color and will be applied to all model elements of the type 'label'.
4.12.1 Defining styles
Styles are defined in the presentation model. Therefore styles are placed into '*.pml' files. It is
supported to append styles in a presentation model file which contains already a component
or they can be put into a separate presentation model file.
63
Presentation Layer Modeling
Best practice: Styles which are intended to be shared between more components should be
placed into a separate presentation model file. To make styles available for a component the
namespace of the presentation model containing the styles have to be imported (like it is done
for other model elements).
4.12.2 Applying styles
To apply a style to a model element and its children set the 'style'-property of the model
element.
Example:
vpanel personPanel style=MyStyle {
label "First name" & text firstName
label "Last name" & text lastName
}
In this example the style 'MyStyle' is set for the model element 'personPanel'. This causes
that the property of the style are applied to the element 'personPanel' and its children when
any of these model elements matches the selector. Because the selector in this case is 'label'
the properties are applied to the labels ('First name' and 'Last name').
4.13 Extending and customizing a page
64
Chapter 5: Domain Model
This chapter introduces the concept of the Domain model and describes the various types a domain
model is composed of.
"To create software that is valuably involved in users' activities, a development team must
bring to bear a body of knowledge related to those activities. The breadth of knowledge
required can be daunting. The volume and complexity of information can be overwhelming.
Models are tools for grappling with this overload. A model is a selectively simplified and
consciously structured form of knowledge. An appropriate model makes sense of information
and focuses it on a problem." 1 [1]
Figure 5.1. MDD
In the following sections we describe each of the supported domain model elements and
explain how they map to the previous figure.
5.1 Entity
"Some objects are not defined primarily by their attributes. They represent a thread of
identity that runs through time and often across distinct representations. Sometimes such an
object must be matched with another object even though attributes differ. An object must be
distinguished from other objects even though they might have the same attributes. Mistaken
1
Evans, 2003, S. 3.
65
Domain Model
identity can lead to data corruption. An object defined primarily by its identity is called an
ENTITY."
Entities map to the same concept in ➧MDD and represents an abstraction of the data and
behaviour of a real-world concept togehter with an unique identity. An entity is composed
of attributes, references, sortorders, finders and one optional key definition as outlined in
the figure below. Entities are identified by name and can have a number of attributes and
references. To support inheritance, an entity can also refer to another entity as its supertype.
Figure 5.2. Entity Structure
5.1.1 Comment
Like most other Domain Model Elements, Entities can be commented with the JavaDoc
Syntax. The comments are adopted in the generated Java code.
66
Domain Model
Figure 5.3. Entity with documentation
5.1.2 Identifier
Entities are required to contain one technical attribute which is designated as id attribute
to distinguish it from other objects. The supported datatypes of this attribute are
• String
• Long
• Integer
and the supported generators are
• assigned this is the default strategy if no generator option is specified and it lets the
application assign an identifier to the object before save() is called.
• identity supports identity columns in DB2, MySQL, MS SQL Server, Sybase and
HypersonicSQL. The returned identifier is of type Long, or Integer.
as shown in the following example.
entity Customer {
id Long oid
}
or with an explicit generator strategy specified.
entity Customer {
id("assigned") Long oid // or id("identity") to use identity columns
}
Its also feasible to inherit the id attribute from an abstract base entity which has the advantage
to reuse it togehter with other commonly used attributes like the version attribute.
entity BaseEntity {
id String oid
version Timestamp ^version
}
entity Customer extends BaseEntity {
String(25) firstName
String(25) lastName
}
Note: the '^' symbol in the example above is used to escape certain reserved names which are
keywords and are otherwise shown as errors when they are used out of context. This pattern
supports the recommendation from the hibernate best practice2 chapter given below:
"Hibernate makes identifier properties optional. There are a range of reasons why you should
use them. We recommend that identifiers be 'synthetic', that is, generated with no business
meaning."
2
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html
67
Domain Model
5.1.3 Version
Besides the id attribute there is another mandatory technical attribute required to support the
data integrity of entities and to prevent lost updates. The pattern of the underlying concept is
known as optimistick locking and was described in Patterns of Enterprise Architecture. 3[2]
The supported datatypes for version attributes are:
•
•
•
•
Long
Integer
Timestamp
Date
Note:Timestamp and Date version attributes are treated in the same way internally and thus
expected to exhibit a milliseconds fraction.
5.1.4 Attributes
An attribute defines a certain feature of the enclosing entity and consists of a type definition,
a name and optional constraints and properties as shown in the next example.
entity Customer extends BaseEntity {
String(25) firstName
required = true
readonly = true
available = true
derived = true
transient = true
constraints = StringValidator[firstName<=30]
format = StringValidator
title= "First name"
description = "First name"
hstore = "hstore column name"
unit = "some unit value"
}
•
•
•
•
•
•
•
•
•
•
required specifies whether the value of the attribute is required (not null) or not.
readonly determines whether the attribute value can be externally set.
available
derived specifies whether the attribute value is to be computed from other related
data
transient determines whether the attribute value is omitted from the persistent
state of the entity to which it belongs.
constraints specifies the constraints applied on the attribute value
format specified the format of the attribute value
title specifies the text string used to label the attribute (applicable in combination
with some presentation model )
description specifies the hover text of the attribute (applicable in combination
with some presentation model )
hstore is only available with PostgreSQL database. The attribute value is the
value of the underlying database HSTORE column name and the property name
(in this example "firstName" is the actual Key value in the HSTORE column. The
key name is case sensitive which means the key in the column must be exactly
"firstName". The raw content of the HSTORE column could be read by entering the
same values for both the name and hstore attributes, in this case hstore = "firstName".
3
Fowler, 2003, S. 416.
68
Domain Model
• unit specifies the unit of the attribute value
Besides the declaration of static boolean literal values as shown above, right-hand-side values
of properties also accept expression language terms to define certain property conditions
which are evaluated at runtime. This term must either be expressed inline together with the
property declaration or 'externally' a as named condition variable inside a context definition
block. The following listing shows an example for named condiditions because this is the
suggested way as it allows to share conditions declarations amongst several properties. As
always it's possible to extract the condition variables into an separate model file which can
be later imported with the classpath URI mechanism described elsewhere.
entity Customer {
String(25) firstName
required = ovA
}
context AppContext {
property String operationVariant
// define named conditions
conditions {
ovA= operationVariant == "A", doc:"Operation variant A is active"
ovB= operationVariant == "B", doc:"Operation variant B is active"
}
}
5.1.5 References
References are used in modeling one end of an association between two involved entities.
The only thing which differentiates an attribute from a reference declaration is the kind of
type used. Since references are used to indicate relations between two involved entities,
references have to start with an entity type followed by the name of the reference (i.e. role
name in UML). If the association should be navigable in both directions, there has to be
an additional reference to represent this bi-directionality indicated with the oppositeof
keyword followed by the name of the opposite reference. The specification of an opposite
reference triggers the creation of additional operations and logic which enforces the correct
'linking' between the two involved entities. This is especially important in combination with
the default hibernate based repository (i.e. Dao) implementation because hibernate requires
the setting on both reference sides to correctly handle the persistence aspect (insert/update)
of those references.
In general we distinguish between two different types of referencens which differ in the level
of the relatedness between the two particapting entities.
Association
The first one are a simple matter of defining a bi- or uni-directional relationship between two
particpating entities where each of the two entities has its own independent lifecycle. This
kind of reference is comparable to the standard uml association semantics. The following
snippet shows one example for a uni-directional reference between Customer and Address
together with a many-valued bi-directional association between Product and Category.
entity Customer extends BaseEntity {
String firstName
String lastName
Address invoiceAddress
required=true
}
entity Address extends BaseEntity {
String streetName
String streetNumber
69
Domain Model
String zip
String city
}
entity Category extends BaseEntity {
String name
String displayName
Product[] products oppositeof category
}
entity Product extends BaseEntity {
String name
Integer unitPrice
Integer unitOnStock
Integer unitOnOrder
Category category
}
Currently we only support java.util.Set as the implementation type for many-valued
(or collection) references. If necessary we will also support the other container types like
List or Map.
Bi-directionallity is declared through the declaration of the oppositeof keyword togehter
with a reference to the opposite reference. Generated mutator/accessor methods, of references
with a declared opposite reference, contain suplementatry code to set and reset the opposite
reference like shown in the next codesnippet.
public void addAddress(Address address) {
if (address == null) {
throw new IllegalArgumentException("parameter 'address' must not be null");
}
if (!getAddress().contains(address)) {
this.address.add(address);
address.setCustomer(this);
}
}
public void removeAddress(Address address) {
if (address == null) {
throw new IllegalArgumentException("parameter 'address' must not be null");
}
if (getAddress().contains(address)) {
this.address.remove(address);
address.setCustomer(null);
}
}
As shown in the previous example it's also feasible to define the persistent state of one-ended
references as mandatory or not-null with the declaration of the required keyword.
Composition
The composition reference type is used to define a kind of by-value aggregation in which
one side of the relation (the container) contains the other (the value). This type of reference
expresses a whole-part relationship where the lifecycle of the contained entity is strongly tied
to the containing entity and the contained value entity cannot, directly or indirectly, contain
its own container entity. A contained entity could have no more than one container entity at
any given time and implicitly is bi-directional. This type of reference compares to an uml
composition and is used to convey additional semantics required for generating certain data
integrity constraints about the persistent state of the entity. (e.g. a removal of the child entity
from the parent entity should automatically trigger a delete from the underlying datastore)
The following example shows the declaration of a containment reference between Customer
and Order entities.
entity Customer extends BaseEntity {
70
Domain Model
String firstName
String lastName
Address invoiceAddress
composition Order[] orders oppositeof customer
}
entity Order extends BaseEntity {
Date placementDate
Date deliveryDate
String orderState
Integer taxes
Customer customer
required = true
}
Note: that the composition keyword must be declared on the containing or owning side
of the relationship. (i.e. the table which doesn't hold the foreign-key column)
Reference examples
This sections cover examples showing the currently supported reference types and how they
map to hibernate concepts.
Note: Currently all references are based upon simple foreign-key relations. Besides bidirectional Many-to-many associations we dont have any support for join tables at the
moment.
The following customer entity has a collection of orders which corresponds to a unidirectional One-to-many association in hibernate.
entity Customer extends BaseEntity {
Order[] orders
}
entity Order extends BaseEntity {
}
To make the previous example bi-directional two additional specifications are needed. First
the order entity has to declare a reference to customer and the customer entity must indicate
the orders collections as the bi-directional opposite end of the order reference.
Note: the oppositeof keyword marks the two involved reference as bi-directional and is
only used on the side which doesnt contain the corresponding foreign key column. (e.g. on
many-valued reference sides) The following examples maps to a bi-directional One-tomany - Many-to-one relation in hibernate.
entity Customer extends BaseEntity {
Order[] orders oppositeof customer
}
entity Order extends BaseEntity {
Customer customer
}
If we set the previous customer reference to required (i.e. not null foreign key) we should also
indicate this fact on the orders reference and mark it as composition since the order entity
cannot exists without a valid customer reference (foreign key).
entity Customer extends BaseEntity {
composition Order[] orders oppositeof customer
}
entity Order extends BaseEntity {
Customer customer
required = true
}
71
Domain Model
The following Product entity has a required (e.g. not-null) uni-directional supplier reference
which maps to a (not-null) Many-to-one relation in hibernate.
entity Product {
Supplier supplier
required = true
}
entity Supplier {
}
The following example show a bi-directional association on a foreign key.
Note: Again you have to indicate the foreign-key side with the declaration of the
oppositeof keyword. In this example the foreign key is on the customer side since we
have marked the customer reference on address as opposite side to the address reference.
entity Customer extends BaseEntity {
Address address
}
entity Address extends BaseEntity {
Customer customer oppositeof address
}
Bi-directional Many-to-many references are also supported but must be augmented with
additional informations in the repository to specify the name of the used join table. The
following example shows a bi-directional reference between product and category.
entity Category extends BaseEntity{
Product[] products oppositeof categories
}
entity Product extends BaseEntity {
Category[] categories
}
repository CategoryDao for Category {
many-to-many products <-> "T_PRODUCT_CATEGORY"
}
repository ProductDao for Product {
many-to-many categories <-> "T_PRODUCT_CATEGORY"
}
Note: With the currently set of supported hibernate reference mapping types we follow the
recommendations from the hibernate best practice4 chapter explained below:
• Do not use exotic association mappings
Practical test cases for real many-to-many associations are rare. Most of the time
you need additional information stored in the "link table". In this case, it is much
better to use two one-to-many associations to an intermediate link class. In fact, most
associations are one-to-many and many-to-one. For this reason, you should proceed
cautiously when using any other association style. However, if you use many-tomany association, use master-slave pattern: Mmark only one side (the slave side)
with "oppositeof". The other side (the master side) then is responsible for managing
database entries.
• Prefer bidirectional associations:
Unidirectional associations are more difficult to query. In a large application, almost
all associations must be navigable in both directions in queries.
4
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html
72
Domain Model
5.1.6 Natural Key
Additional to the required technical identifier or key it's also worth achieving to identify
natural keys for all entities. A natural key is a feature or combination of features that is
unique, non-null and immutable. A natural key is comparable to an alternate or secondary
key in relational database design. A natural key declaration will trigger the creation of some
additional dataaccess operation wihin the particular dataaccess provider (which is by default
a Dao) to access the respective entity given the set of unique features.
A natural key starts with the key keyword and name followed by an enumeration of attributes
and (to-one) references which identifiy one particular entity instance. An entity can contain
at most one natural key.
entity Customer extends BaseEntity {
String firstName
String lastName
Date
birthDate
key CustomerNk(firstName,lastName,birthDate)
}
5.1.7 Unique Keys
A unique key declaration indicates that a certain set of attributes can be used to uniquely
identify a particular entity instance. Unique key starts with the unique keyword and
name followed by an enumeration of attributes and (to-one) references which identifiy one
particular entity instance.
entity Customer extends BaseEntity {
String firstName
String lastName
Date birthDate
unique UniqueCustomer(firstName,lastName,birthDate)
}
5.1.8 Sort Orders
A sort order starts with the sortorder keyword and speficies a named, reusable set of
attributes and sort directions pairs used to sort to-many references.
entity Customer extends BaseEntity {
String firstName
String lastName
Address invoiceAddress
composition Order[] orders oppositeof customer orderby PlacementDateAsc
}
entity Order extends BaseEntity {
Date placementDate
Date deliveryDate
String orderState
Integer taxes
Customer customer
required = true
sortorder PlacementDateAsc (placementDate asc)
}
5.1.9 Generated Artefacts
The default openXMA workflow and templates will create the following variations of
generated and manual code for each model entity.
73
Domain Model
• a generated entity (EntityGen) interface and abstract entity implementation
(EntityGenImpl) within the generated src folder
containing the attribute fields and accessor/mutator method implementations
• an empty entity interface (Entity) and concrete entity implementation (EntityImpl)
derived from the generated ones and within the manual src folder
these artefacts are only generated for the first time (i.e. no Entity and EntityImpl found
in the manual src folder) and usually contain the additional manual implementation
code
The following figures illustrates the generation gap pattern on the basis of generated entity
artefacts
74
Domain Model
Figure 5.4. Entity artefacts
75
Domain Model
5.1.10 Syntax Diagram
76
Domain Model
5.2 Repository
Most applications nowadays are using relational databases as the primary means to store and
query their business data. Since entities are used as an abstraction for business (domain) data
and behaviour its obvious to provide some mechanism to map these entities to a persistent
database representation and to provide some querying facilities. This is what the repository
model element is used for. Up to now the generator only supports hibernate based repository
implementations and expects the repository declaration to be in the same model (file) as the
corresponding entity.
A repository element starts with the repository keyword followed by a name and a reference to
the entity this provider is mapped to.The following example shows a fairly complete example
of the currently supported repository mapping elements and how they relate to the referenced
entity. We will refer to this example when we are going to explain each of the currently
possible elements in the following sections.
77
Domain Model
Note: For the sake of completness he previous figure shows all possible features of repository
elements. Many of them are optional or automatically derived from a namingstrategy or from
some information already available in the entity corresponding element and doesnt have to
be explicitly defined.
A repository element accepts the following attributes.
• a required for [Entity] specifies the entity this repository is used for
78
Domain Model
• an optional table specifies the name of its database table
Note: this is only required if the name provided from the configured NamingStrategy
has to be overriden
• an optional qualifier specifies a string that distinguishes individual subclasses in
a polymorphic relationship
Note: A qualifier value maps to the discriminator-value from the table-per-class
inheritance mapping stratgey of hibernate. Right now this is the only supported
inheritance mapping strategy.
• an optional discriminator specifies the name of the column which holds the
qualifier value
5.2.1 Operation
Operations come in two flavours. The first one is only used to create the signature and a
placeholder for data access code which must be manually implemented as shown in the listing
below. Manual operations are used in all cases where QL operations (shown next) are not
sufficient enough.
operation CustomerContactReport[] loadCustomerReport(String name)
The declaration of the operation shown above generates the following signature and empty
method body which has to be implemented manually.
Note: The generation of an empty method body in the manual CustomerDaoImpl actually
happens only the first time if the file doesn't exist yet.
// in CustomerDaoGen
Collection<CustomerContactReport> loadCustomerReport(String name);
// in CustomerDaoImpl
public Collection<CustomerContactReport> loadCustomerReport(String name) {
return null;
}
5.2.2 Query Operations
Query operations look alike manual operations with the additional specification of a hibernate
query language5 (and the subset standardized as JPA QL) statement. A particualar advantage
of this kind of operation is the instant syntax verification and content assist for the given
query string and parameters.
operation Customer[] findAllByFirstNameAndLastName(String firstName,String
lastName) :
from Customer c
where
c.firstName like :firstName and
c.lastName like :lastName
The declaration of this query operation creates the following code within the generated
DaoGenImpl class.
public Collection<Customer> findAllByFirstNameAndLastName(String firstName,String
lastName) {
Query namedQuery = this.sessionFactory.getCurrentSession().
getNamedQuery("Customer.FindAllByFirstNameAndLastName");
namedQuery.setParameter("firstName", firstName);
namedQuery.setParameter("lastName", lastName);
applyFindAllByFirstNameAndLastNameQueryHints(namedQuery,firstName,lastName);
return list(namedQuery);
5
http://docs.jboss.org/hibernate/stable/core/reference/en/html/queryhql.html
79
Domain Model
}
protected void applyFindAllByFirstNameAndLastNameQueryHints(Query query,String
firstName,String lastName) {
// override this method and apply other hints that influence how a query is
executed
}
The query statement gets generated as an externalized string into the mapping configuration
file of the corresponding entity. The name of the query in the mapping document gets derived
from the entity and operation name.
<query name="Customer.FindAllByFirstNameAndLastName">
<![CDATA[
from CustomerImpl as c
where c.firstName like :firstName and c.lastName like :lastName
]]>
</query>
We support the following kinds of query statements:
• delete statements
operation /* optional Integer */ deleteAllCustomersLikeName(lastName) :
delete from Customer as customer
where customer.lastName = :lastName
• update statements
operation updateCustomer(String newName,String oldName) :
update Customer customer set
customer.name = :newName
where customer.name = :oldName
// according to the EJB3 specification, HQL UPDATE statements, by default,
do not effect the version or
// the timestamp property values for the affected entities..
// .. force Hibernate to reset the version or timestamp property values
through the use of a versioned update
operation updateCustomerVersioned(String newName,String oldName) :
update versioned Customer customer set
customer.name = :newName
where customer.name = :oldName
• insert statements
insert into Customer ( firstName , lastName )
select
customer.firstName,
customer.lastName
from Customer customer
where
customer.clientId = :clientId
• select statements returning entities or scalar values
operation Customer[] findAllByFirstNameAndLastName(String firstName,String
lastName) :
from Customer c
where
c.firstName like :firstName and
c.lastName like :lastName
operation Customer findCustomerByOid(oid) :
from Customer as customer
where
customer.oid = :oid
80
Domain Model
operation String[] getAllCustomerNames() :
select
distinct customer.lastName
from Customer customer
• select statements returning projected dataviews
dataview CustomerNameReport {
Integer groupCount
Customer.lastName
}
.
.
operation CustomerNameReport[] loadCustomerNameReport() :
select
customer.lastName,
count(customer) as groupCount
from Customer customer
group by customer.lastName
The last select statement is a so called report query which let you specify which attributes to
retrieve . Since report queries doesnt returned managed entity instances but only data tuples
the generator needs to create some additional mapping code to map from those tuples to the
specified query return type like the following one.
protected CustomerNameReport mapLoadCustomerNameReportTuple(Object[] tuple, int
rowNumber) {
CustomerNameReport customerNameReport = new CustomerNameReport();
customerNameReport.setLastName((String)tuple[0]);
customerNameReport.setGroupCount((Integer)tuple[1]);
return customerNameReport;
}
Note: This pattern supports the recommendation from the hibernate best practice6 chapter
given below:
"Consider externalizing query strings: This is recommended if your queries call non-ANSIstandard SQL functions. Externalizing the query strings to mapping files will make the
application more portable."
5.2.3 Callable Statement Operation
Callable Statement operations support the execution of Sql Stored Procedures and Functions.
They allow for the specification of IN and OUT (IN-OUT) parameters combined with the
mapping of complex structures passed into or returned from the declaring operation as shown
in the examples below. The general structure of callable statement operations is outlined in
the following figure.
6
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html
81
Domain Model
Examples of input and output parameter mappings
The following section gives an overview of the currently supported options of callable
statement usage
• basic call with and without package name
repository CustomerDao for Customer {
operation spWithoutPackageName() :
call sp_foo()
operation spWithFooPackageName() :
call FOOPACKAGE.sp_foo()
}
• stored procedure call without any input- or output parameter
repository CustomerDao for Customer {
operation spWithoutParameters() :
call sp_foo()
}
82
Domain Model
• sp call with single input parameter and no output parameter
repository CustomerDao for Customer {
operation spWithParameters(String name) :
call sp_foo(name)
}
• with single input parameter and name aliasing (and no output parameter)
repository CustomerDao for Customer {
operation spWithNameAliasing(String name) :
call sp_foo(name as IN_NAME)
}
• with multiple input parameters with and without name aliasing
repository CustomerDao for Customer {
operation spWithMultiInParameter(String name,Date fromDate,Integer
maxLimit) :
call sp_foo(name as IN_NAME,fromDate as IN_DATE,maxLimit)
}
• with complex type input parameter
repository CustomerDao for Customer {
operation spWithComplexTypeInParameter(FooInput fooInputParameter) :
call sp_foo(fooInputParameter.name as IN_NAME,fooInputParameter.
fromDate as IN_DATE,fooInputParameter.maxLimit)
}
dataview FooInput {
String name
Date fromDate
Integer maxLimit
}
• function call with scalar return type
repository CustomerDao for Customer {
operation Integer functionWithScalarReturnType(Date fromDate,Date
toDate) :
call function foo_function(fromDate,toDate)
}
• function call with scalar return type and explicit output parameter mapping
repository CustomerDao for Customer {
operation Integer functionWithExcplicitOutParameterMapping(Date
fromDate,Date toDate) :
call function foo_function(fromDate,toDate)
return OUT_COUNT
}
• stored procedure call which returns a collection of some scalar return type
repository CustomerDao for Customer {
operation Integer[] spWithScalarCollectionReturn(Date fromDate,Date
toDate) :
call get_customer_ids(fromDate,toDate)
return CUSTOMER_OID
}
• stored procedure call with complex type output mapping
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date
toDate) :
call get_customer_ids(fromDate,toDate)
return rowCount,
created,
name
}
dataview FooOutput {
Integer rowCount
83
Domain Model
Date created
String name
}
• stored procedure call with complex type output mapping and out parameter name
aliasing
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date
toDate) :
call get_customer_ids(fromDate,toDate)
return OUT_ROW_COUNT as rowCount,
OUT_DATE_CREATED as created,
OUT_CUSTOMER_NAME as name
}
dataview FooOutput {
Integer rowCount
Date created
String name
}
• stored procedure call with complex type collection output mapping
repository CustomerDao for Customer {
operation FooResult[] spWithComplexTypeOutputMapping(Date fromDate,Date
toDate) :
call get_customer_ids(fromDate,toDate)
return created,
name
}
dataview FooResult {
Date created
String name
}
• stored procedure call with nested complex type collection output mapping
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date
toDate) :
call get_customer_ids(fromDate,toDate)
return rowCount,
results.created,
results.name
}
dataview FooOutput {
Integer rowCount
FooOutput[] results
}
dataview FooResult {
Date created
String name
}
• stored procedure call with nested complex type output mapping
repository CustomerDao for Customer {
operation FooOutput spWithComplexTypeOutputMapping(Date fromDate,Date
toDate) :
call get_customer_ids(fromDate,toDate)
return rowCount,
result.created,
result.name
}
dataview FooOutput {
Integer rowCount
FooOutput result
}
84
Domain Model
dataview FooResult {
Date created
String name
}
The generated body of an callable statement operation uses spring SimpleJdbcCall
abstraction for the actual stored procedure or function call. Those operations are further split
up into three protected subcalls which allow for better customization and organization
of the generated code. The following section lists the generated subcalls together with an
example and short explanation of each
1. prepare%OPERATION_NAME%JdbcCall
protected SimpleJdbcCall prepareSpWithComplexTypeOutputMappingJdbcCall(Date
fromDate,Date toDate) {
SimpleJdbcCall jdbcCall =
createJdbcCall(SP_SP_WITH_COMPLEX_TYPE_OUTPUT_MAPPING);
return jdbcCall;
}
The purpose of prepare%OPERATION_NAME%JdbcCall is to simply create and
setup the SimpleJdbcCall instance of the actual stored procedure or function call. By
default this operation returns a new SimpleJdbcCall instance for every invocation.
Since SimpleJdbcCallis designed as reusable (multi-threaded) object an overriden,
customized implementation of prepare%OPERATION_NAME%JdbcCall could for
example return a cached SimpleJdbcCall instance for repeated invocations. (or mock
for unit tests) Another performance related optimization version could turn-off the default
meta data processing of SimpleJdbcCall and explicitly declare all input- and output
parameter names together with their matching Sql type as shown in the following example.
Also, if you need to pass any database-vendor specific types (e.g. oracles Array datatype)
you probably have to override this implementation too.
@Override
protected SimpleJdbcCall prepareSpWithComplexTypeOutputMappingJdbcCall(Date
fromDate, Date toDate)
if (this.jdbcCall == null) {
jdbcCall = super.prepareSpWithComplexTypeOutputMappingJdbcCall(fromDate,
toDate);
jdbcCall.withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlParameter("IN_FROM_DATE", Types.DATE))
.declareParameters(new SqlParameter("IN_TO_DATE", Types.DATE))
.declareParameters(new SqlOutParameter("OUT_FIRST_NAME", Types.VARCHAR));
}
return jdbcCall;
}
2. execute%OPERATION_NAME%
protected Map<String, Object>
executeSpWithComplexTypeOutputMapping(SimpleJdbcCall jdbcCall,Date fromDate,Date
toDate) {
Map<String,Object> parametersMap = new HashMap<String,Object>();
parametersMap.put("fromDate",fromDate);
parametersMap.put("toDate",toDate);
return executeJdbcCall(jdbcCall,parametersMap);
}
Uses the previously created SimpleJdbcCall instance to execute the jdbc call with a
map of configured parameter names and values. Override this method to provide default
parameters or values. (override #executeJdbcCall to handle all jdbc calls of a given
repository)
3. map%OPERATION_NAME%Result
protected FooOutput mapSpWithComplexTypeOutputMappingResult(Map<String, Object>
_resultMap,Date fromDate,Date toDate) {
85
Domain Model
FooOutput _fooOutput = new FooOutput();
_fooOutput.setRowCount((Integer)_resultMap.get("rowCount"));
FooResult _result = new FooResult();
_fooOutput.setResult(_result);
_result.setCreated((Date)_resultMap.get("DAT_CREATED"));
_result.setName((String)_resultMap.get("NAM_LAST_NAME"));
return _fooOutput;
}
Only for callable statements with a declared return type. Receives the result of a
preceeding jdbc call invocation as generic map structure and converts from map entries
to the specified return type based on the output parameter mapping of the corresponding
return statement.
5.2.4 Columns
Columns map attributes to respective columns and only have to be explicitly specified if
1. the calculated column name from the configured (or default) naming strategy does not
match (and you don't want to implement a custom naming strategy)
entity Customer extends BaseEntity {
String ssn
}
repository CustomerDao for Customer {
column ssn <-> "SOCIAL_SECURITY_NUMBER" // instead of SSN
}
2. the default column or hibernate type must be overriden or no sensible defaults can be
infered automatically like in the following example as the newletter boolean has to be
mapped with a special usertype which persists the boolean value as y/n characters.
Note: there is also another possibility with the openXMA generator preferences within
eclipse to map usertype's to attribute types within a workspace or project
entity Customer extends BaseEntity {
Boolean newsletter
}
repository CustomerDao for Customer {
column newsletter usertype=YesNoType
}
For the first case there are actually two ways how to map attributes to column names. One can
either explicitly declare the attribute mapping with the column keyword (as already shown
above) or adapt and configure the namingstrategy which the generator will use to provide
the name in the automatically created column elements.
Columns also support the nested mapping of multi-valued attribute types like Address or
Money like in the following mapping scenario.
valueobject Money {
String currency
Integer amount
}
entity Customer extends BaseEntity {
Money money
}
repository CustomerDao for Customer {
column money {
column currency <-> "MONEY_CURRENCY"
column amount <-> "MONEY_AMOUNT"
}
}
86
Domain Model
5.2.5 Many-to-one
Used to map an ordinary reference or containment (cardinality 1 or 0) defined in the enclosing
entity to a foreign-key (constrained) column of another table (entity).
repository CustomerDao for Customer {
many-to-one invoiceAddress <-> "ID_INV_ADRE"
}
Many-To-One elements support the following optional attributes:
• <-> the name of the foreign-key column
Usually it's not required to explicitly declare this many-to-one element (besides to
explicitly override and set the above mentioned properties) because it (i.e. the table which
holds the foreign key) can always be derrived from the one-ended reference end as shown
in the next example.
entity Customer extends BaseEntity {
String firstName
String lastName
String ssn
Boolean premiumMember
Address address
}
entity Address extends BaseEntity {
String(30) streetName
String streetNumber
String(10) zip
Customer customer oppositeof address
}
In this bi-directional Customer - Address relation the generator can derive the many-toone side from the cardinality info (zero or many address vs. zero or one customer) of the
two involved references. In a uni- or bidirectional one-to-one relation the following rules are
applied to derive the many-to-one side
• specification of an opposite reference which is either marked as containment or
multi-valued (i.e. collection) and which isnt required
• the reference is marked as required
• existence of an explicit many-to-one element within the associated provider
element
• the reference is a unidirectional reference (e.g. unidirectional one-to-one
association on a foreign key)
5.2.6 One-to-many
Used to map a collection-valued (with list or set collection type) reference or containment
to as persistent relation.
provider CustomerDao for Customer {
one-to-many orders <-> "ID_CUST"
}
One-To-Many elements support the following optional attributes.
• <-> the name of the (opposite) foreign-key column
As already stated in the previous section about many-to-one mappings it is not necessary
to explicitly declare this one-to-many element since the generator can derive this information
from the 'oppositeof' reference declaration.
87
Domain Model
5.2.7 One-to-one
Used to map the opposite role in a bi-directional one-to-one association. This is the opposite
end of the association containing the foreign-key (many-to-one) as shown in the following
example. The only reason up to now to declare this mapping element expliciltly is to
manually set the cascade behaviour. For all other bi-directional one-to-one (to one-to-many)
association mapping its NOT required to declare this element since the generator can infer
it automatically. By default the generator infers the 'one-to-one side' from the opposite
reference declaration of a bi-directional relationship like so.
entity Customer extends BaseEntity {
String firstName
String lastName
String ssn
Boolean premiumMember
ref Address address
}
entity Address extends BaseEntity {
String(30) streetName
String streetNumber
String(10) zip
Customer customer oppositeof address
}
Or to put it in another way the one reference WITHOUT an existing opposite declaration
(address) is always assumed to be the many-to-one side (i.e. the table which holds the
foreign key) within a bi-directional one-to-many or one-to-one relation. Because of this rather
limited usage we are currently evaluating to deprecate or remove this element from the dsl.
5.2.8 Generated Artefacts
For each repository element two or more java classes and three hibernate mapping files are
generated. The first java file is the DaoGen interface which extends a generic Dao interface
from the platform library and provides several ready to use factory and crud methods. One
factory method without parameter and an additional factory method taking all required
attributes and (to-one) references to provide a consistent way of entity construction. The
second java file represents the actual hibernate based implementation which implements the
generated interface.
The three hibernate mapping files are denominated with three different suffixes:
• ".hbm.xml": the actual merged mapping file used at runtime (location: project folder
for generated resources ("src-gen" or "src/generated/resources"))
• ".hbm.fragment.xml": the mapping file used to configure hibernate mapping
customisation (location: project folder for source resources ("src" or "src/main/
resources"))
• ".hbm.gen.xml": the raw generated mapping file, without merged in customisations,
derived from the entity model only (location: project folder for generated resources
("src-gen" or "src/generated/resources"))
Explanation: Hibernate mapping elements found in the fragment file, will be merged into the
hbm.xml. Elements in hbm.xml, which also exist within the fragment will be overwritten by
the ones in the fragment.
This empowers the developer to further customise the generated hibernate mapping file used
at runtime, which would otherwise not be possible only by the means of the entity model.
The reason why is, that hibernate offers a wide array of configuration options and it dos not
make sense to abstract this on a model level. The hbm.gen.xml file should not be used at
88
Domain Model
runtime as it does not reflect the customisations made in the fragment file. It merely works as
a reference for the raw generated hibernate mapping file content. It will be generated every
time the generator workflow is started, like the hbm.xml file.
After the generator workflow has generated the hbm.xml and hbm.gen file, it searches for a
fragment file. If it found none, it will create one. If it found one, it will check, if there are
elements to be merged into the hbm.xml file, keeping the hbm.gen file untouched. Therefore
the developer has to be aware of his or her customisations made in the fragment file, as they
overrule the mapping elements in the generated hbm.xml file.
Note: The generation of a separate mapping configuraton document for each entity supports
the recommendation from the hibernate best practice7 chapter given below:
"Place each class mapping in its own file: Do not use a single monolithic mapping document.
Map com.eg.Foo in the file com/eg/Foo.hbm.xml. This makes sense, particularly in a team
environment."
7
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html
89
Domain Model
Figure 5.5. Provider artefacts
90
Domain Model
5.2.9 Syntax Diagram
91
Domain Model
5.3 ValueObject
"When you care only about the attributes of an element of the model, classify it as a VALUE
OBJECT. Make it express the meaning of the attributes it conveys and give it related
functionality. Treat the VALUE OBJECT as immutable. Don't give it any identity and avoid
the design complexities necessary to maintain ENTITIES." 8 [1]
A value object is used to encapsulate semantically related attributes as fine grained classes.
Figure 5.6. ValueObject Structure
ValueObjects are used entity attributes types to encourage code reuse and to simplify
refactoring. A ValueObject starts with the valueobject keyword followed by a name like
so:
valueobject Address {
String streetName
String streetNumber
String zip
String city
}
Value objects can be used as the type of an entity attribute as shown the next example.
entity Customer {
String firstName
String lastName
Date birthDate
String ssn
Boolean premiumMember
Address invoiceAddress
}
Note: ValueObject supports the recommendation from the hibernate best practice9 chapter
given below:
"Write fine-grained classes and map them using <component>: Use an Address class
to encapsulate street, suburb, state, postcode. This encourages code reuse and simplifies
refactoring."
5.3.1 Generated Artefacts
For each value object element one java class is generated.
8
Evans, 2003, S. 105.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html
9
92
Domain Model
Figure 5.7. ValueObject artefacts
93
Domain Model
5.3.2 Syntax Diagram
94
Domain Model
5.4 DataView
DataViews are used to declare multiple data centric views of your entities required for
different use-case (or context) and client.
Why do we need data views?
Imagine the standard use case of an user interface with a search form, result list and detail
form. If you think about this rather simple scenario and how many data representations of one
and the same data concept (entity) is required you would roughly come up with the following:
• one structure to hold the entity attributes the user wants to search for (e.g.
ClientSearchView)
• one structure containing the condensed entity attributes of the table list (e.g.
ClientListView)
• another (editable) structure to display more details about the entity (e.g.
ClientDetailView)
One could argue that this is a maintenance nightmare and that its simpler to always reuse one
superset structure containing all attributes for every use-case. Altough this approach certainly
works we are thinking a little bit different about this problem because of the following lessons
learned:
• ever worked with legacy structures with >100 attributes and you didnt want to read and
shuffle all attributes between all your layers everytime? (remote even more worse )
• ever wondered why the attribute value is null or if it simply hasn't been fetched from
the database by some method?
• uneasy
feeling
seeing
the
depedencies
on
your
internal
(model,data,domain,whatsoever) packages in the external service api because you
used it as the return types for your clients
• OSIV, lazy loading,layering,...
Therefore we think its best to have a separate structure for each use-case because
• there is no more ambiguity about the (value)state of your data
• we dont have the maintainenace nightmare since we are using a generative approach
(code becomes cheaper now)
• we only expose the data the client really needs
• no more internals on your external service layers anymore
• provides a strict contract with the client of the service about which attributes are
available
We call this structure data views because that is what they really are. A dataview definition
starts with the dataview keyword followed by a name and is shown in the following
example.
// dataview of entity 'Customer' including all attributes of Customer
// Note: actually you DONT have to declare such views because the default generator
templates create so called default dataviews for each entity
// which look alike the following dataview definition. The name of the default view
is always <<EntityName>>View
dataview CustomerView {
Customer.*
}
95
Domain Model
// dataview of entity 'Customer' with one 'local' attribute and two included
customer attributes
dataview CustomerSearchView {
String sortKey
Customer.lastName
Customer.birthDate
}
// dataview of entity 'Customer' which reference anothers dataview
dataview CustomerOrderView {
Customer.lastName
Customer.birthDate
Customer.invoiceAddress<AddressView> // reference mapping with explicit dataview
Customer.billingAddress // reference mapping with the default address dataview
i.e. AddressView
}
// default dataview of entity 'Address'
dataview AddressView {
Address.*
}
Default Entity DataView
Besides the manually defined dataviews the default ModelModifier will also create an
implicit DataView (we call this the default entity DataView) for each entity declaration
which automatically includes all required simple typed attributes (String,Integer,..) and the
to-one relations (or rather the ForeignKey to the related entity) of the entity from which the
DataView gets derived. The name of this automatically created DataView is deduced from
the entity and by default ends with a View suffix. (e.g. Entity Customer will automatically
create a DataView named CustomerView) Nevertheless, sometimes its required to include
additional attributes (besides the aforementioned) in the default DataView which can be
easily achieved by means of manual declaration of a DataView with the same name as the
default DataView. The existence of such a DataView within the same model file as the
corresponding entity prevents the implicit creation and can be used to modify (only addition)
the structure of the default DataView.
default entity dataview nameCustomerViewentity Customer {
String firstName
String lastName
String ssn
Address address
Order[] orders
}
// the existence of a dataview with the
implicit one
prevents the automatically creation of the
dataview {
Customer.* // this is the default behaviour and can be omitted if the default
fits your need = include all simple type attributes and all to-one entity
references
// since orders is a many valued (collection) entity reference type its not
included in the default DataView and we want to overrule this standard behaviour
here
Customer.orders
}
Note: With the concept of DataViews we support the recommendation from the hibernate
best practice10 chapter given below:
"Use the open session in view pattern, or a disciplined assembly phase to avoid problems
with unfetched data: ...Unless you are prepared to hold the persistence context (the session)
10
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html
96
Domain Model
open across the view rendering process, you will still need an assembly phase. Think of your
business methods as having a strict contract with the presentation tier about what data is
available"
Mapping entity references
By default only the identity values of referenced entities are mapped in dataviews. If the real
dataviews of the referenced entities should be mapped as well one has just to specifiy an
additional property in the dataview definition: the property of the entities reference. Here is
an example, which results in a CustomerOptionalForeignKeyTestDto class with an addtional
property named optionalorder, whose name could be altered by the as keyword in the dsl,
of type OrderOfCustomerView (the default dataview) instead of only having a String
property holding the identity value of the referenced OptionalOrder by default:
entity Customer {
id String oid
version Date ^version
String(25) firstName
required = true
title = "First name"
String(25) lastName
required = true
title = "Last name"
composition OrderOfCustomer optionalOrder
oppositeof customer
}
dataview CustomerOptionalForeignKeyTestDto {
Customer.all
Customer.optionalOrder
}
If another dataview should be mapped to than the default dataview, for instance the
OrderOfCustomerLightView, then change the definition of the dataview like in the
following example:
dataview CustomerOptionalForeignKeyTestDto {
Customer.all
Customer.optionalOrder <OrderOfCustomerLightView>
}
5.4.1 Generated Artefacts
For each dataview element one java class is generated and this java class contains all the
attributes and references included from the corresponding entity.
Note: There is currently no built-in support to customize this behaviour without to change
the default provided workflow and templates.
97
Domain Model
Figure 5.8. DataView artefacts
98
Domain Model
5.4.2 Syntax Diagram
99
Domain Model
5.5 Mapper
A mapper maps the properties of 2 different dataviews. A mapper has a name, a left and a
right dataview and in between of the two dataviews it has a mapping direction. The following
figure illustrates these minimum reuqirements of a mapper:
Figure 5.9.
By default, a mapper maps only those attributes which match by their name, irrespective
of their datatypes. A string property could be mapped to another string property or to an
Integer property if only the two properties match by name. The default mapping could be
overridden by so called property mappings within the body of a mapper. As soon as one or
more property mappings are defined, the default mapping behaviour of the mapper is disabled
and only the mapping rules defined with the help of the current property mappings count. A
property mapping consists of a left attribute, taken from the left dataview of the mapper, a
mapping direction and a right attribute, taken from the right dataview of the mapper. Here
is an example:
100
Domain Model
Figure 5.10.
Property Mappings must be consistent. Therefore, the same attribute either on the left or right
side, must not be mapped twice by one or more opposite attributes. Or in other words: There
must only be one property mapping which points the mapping direction to one attribute.
Otherwise an attribute may be mapped twice, which does not make sense. In the following
example, the left attribute emailAddress is mapped twice by the same attribute on the right
side called emailAddress. Although one could consider this particular case as a redundancy
the framework makes an error:
Figure 5.11.
The next figure shows an inconsistency without possible redundancy, becaus the left-hand
attribute emailAddress is mapped twice by the attributes lastName and emailAddress:
101
Domain Model
Figure 5.12.
5.5.1 DataType Conversion
Because the default mapper behaviour maps between different datatypes, the generated
mapper logic is capable of converting datatypes. Here is a taxative list of datatypes and their
convertable counterparts:
• Integer maps to (no mapping of Integer to: Boolean, Date, Timestamp):
• String
• BigDecimal
• Double
• Float
• Integer
• Long
• Date maps to (no mapping of Date to: Integer, Double, Float, Boolean):
• String
• Date
• BigDecimal
• Long
• Timestamp
• BigDecimal maps to (no mapping of BigDecimal to: Boolean):
• String
• Date
• BigDecimal
• Double
• Float
• Integer
• Long
• Timestamp
• Boolean maps to:
• Boolean
• String
outcome: "true" or "false"
• Double maps to (no mapping of Double to: Boolean, Date, Timestamp):
102
Domain Model
•
•
•
•
•
•
String
BigDecimal
Double
Float
Integer
Long
• Float maps to (no mapping of Float to: Boolean, Date, Timestamp):
• String
• BigDecimal
• Double
• Float
• Integer
• Long
• Long maps to (no mapping of Long to: Boolean):
• String
• BigDecimal
• Double
• Float
• Integer
• Long
• Timestamp
• Date
• Timestamp maps to (no mapping of Timestamp to: Integer, Double, Float, Boolean):
• String
• Date
• BigDecimal
• Long
• Timestamp
• String maps to:
• Boolean
(true Strings: "true", "yes", "y", "on", "1")
•
•
•
•
•
•
•
•
(false Strings: "false", "no", "n", "off", "0")
String
BigDecimal
Double
Float
Integer
Long
Timestamp
Date
103
Domain Model
If the mapper is not able to execute a datatype conversion between two different datatypes it
will throw an org.apache.commons.beanutils.ConversionException.
5.5.2 How To Call A Mapper
These are the steps to call a mapper:
• inject the org.openxma.dsl.platform.service.Mapper interface:
@Autowired private Mapper mapper;
• create a java.util.Map with a java.lang.String as the key and an optional
object of type org.openxma.dsl.platform.service.MapperConfig as
the value. The key must be of the following pattern: "mapper:"+mapperName.
The mapper name is specified in the dom model. The MapperConfig should be used
in case when the mapper maps from Date,Timestamp to String or vice versa!
• call any suitable method on the injected Mapper which takes a param named context,
and put the created map from above as the methods context param
Example:
104
Domain Model
Figure 5.13.
5.6 Service
"A SERVICE is an operation offered as an interface that stands alone in the model, without
encapsulating state, as ENTITIES and VALUE OBJECTS do. SERVICES are a common
pattern in technical frameworks, but they can also apply in the domain layer." 11 [1]
11
Evans, 2003, S. 105.
105
Domain Model
A Service maps to the same concept in ➧MDD and represents the service layer of a
domain and are used as the external entry point to an application's business logic. Services
are copposed of normal (to be manually implemented) operations and so called 'delegate
dataaccess operations' which are used to operate on DataViews retrieved and converted
from internal entity access methods (i.e. finders and dataaccess operations). Since the
default generator doesn't support the returning or passing of entity type structures from
or to the external service layer it will replace all entity references with the corresponding
default DataView instead. For example if an operation (i.e. exported repository or
manual operation) has an entity return type or declares an entity type parameter it will be
automatically replaced with the default DataView in the generated source code.
Figure 5.14. Service Structure
Service operations map to indiviudal use cases of the domain with the following functional
and non-functional characteristics
• transaction demarcation based on the respective use-case demands
• security (➧RBACL)
• logging, journaling and auditing (maybe compliance with any revision demands)
• any means to create/provide necessary context/state (e.g. client language or
locale,user,roles) objects for the called operation
• validation of incoming state
• business logic
An example of a service expressed with the domain model dsl is given in the following listing
which defines a service named OrderReportService with two required dependencies and one
abstract service operation.
// Represents a service used to generate and mail an aggregated report of customer
orders for the given year and month
service OrderReportService uses OrderDao, EmailService {
operation generateAndMailOrderReport(YearMonth yearMonth, String emailAddress)
}
106
Domain Model
5.6.1 Naming
The OrderReportService example above contains a few important concepts which also apply
to other model elements. First and foremost it gives the service a name: 'OrderReportService'.
The name is a required identifier token12 and primarily used for the following things:
• to reference this service by name from within another service which depends on the
'OrderReportService' , like it is done with the 'uses EmailService' in the example
above.
• the name (in combination with the containing package folder) is used as the default
name of the generated java service interface and java implemenation.
• it is used as the identifier of the corresponding spring bean configuration generated
by default.
5.6.2 Dependencies
The next thing to note is the uses keyword. The uses keyword defines a dependency
to another Service or DataAccessObject which are required to implement the behaviour of
the user defined 'generateAndMailOrderReport' operation. If you declare a dependency to
another Service or ➧DAO the default generator templates provides you with the following:
• correct wiring of the stated dependencies based on the mechanism provided by the
spring13➧IOC
• a member variable of the given dependency type in the generated service
implementation class
5.6.3 Operations
Services allow to define so called abstract service operations as a placeholder for
custom behaviour which has to be implemented manually within concrete service class
implementations. Service operations start with the operation or op keyword followed by
a name and and optional list of input parameters.
service OrderReportService uses OrderDao,CustomerDao,EmailService {
operation generateAndMailOrderReport(YearMonth yearMonth, String emailAddress)
}
service OrderServiceFacade uses CustomerDao {
operation CustomerOrderView findByCustomerOid(String oid)
operation OrderStateView[] findAllOrderStates(String oid)
OrderStateView Order.findByOrderNumber
}
service EmailService {
operation sendEmail(String emailAddress, String subject, String text)
}
5.6.4 Dataaccess Operations
In addition to operations which must be manually implemented (s.a.), services also support
the notion of fully generated dataacess operations used to access and modify entities
via dataviews. The definition of such an operation consists of a (matching) dataview
returntype, an repository reference, an access operation type or reference and an optional
12
http://www.openarchitectureware.org/pub/documentation/4.3.1/html/contents/
xtext_reference.html#xtext_reference_grammar_language_tokens
13
http://static.springframework.org/spring/docs/2.5.x/reference/beans.html
107
Domain Model
dataview parameter. Dependent on the particular type of access operation the return type
and/or view parameter is required or optional. (e.g. a read operation must always define a
dataview return type otherwise the model is considered invalid and will not be processed
correctly). The following listing gives an example of standard crud dataaccess operations
to read and modify Customer entities.
Note: dont go with crud as the default. only specify whats actually required from yout usecases
service CustomerService {
Customer.create(CustomerView)
CustomerView Customer.read
Customer.update(CustomerView)
Customer.delete
}
A shortcut syntax for the generation of all individual dataaccess operation types
(create,update,delete,read) is available with the crud keyword. If create and
update (or crud) is defined, a saveOrUpdate functionallity will be generated as well.
service CustomerService {
Customer.crud
}
Note: Prior to including a repository data access operation it has to be declared in the
referenced repository first like in the following example.
entity Customer {
id String oid
version Timestamp ^version
String(25) firstName
String(25) lastName
String(40) emailAddress
Date("Medium") birthDate
}
repository CustomerDao for Customer {
operation Customer findByEmailAddress(emailAddress):
from Customer customer where customer.emailAddress = :emailAddress
}
service CustomerService {
CustomerDao.findByEmailAddress
}
Actually this operation does two things: First it delegates to the included finder operation on
the customer repository (with the given emailAddress parameter) and afterwards it converts
the entity result to the default (implicitly created) customer dataview. It is also possible to
override entity return types of included repository operations with an arbitrary dataview as
long as the specified dataview includes (maps) attributes of the returned entity as shown in
the following example.
entity Customer {
id String oid
version Timestamp ^version
String(25) firstName
String(25) lastName
String(40) emailAddress
Date("Medium") birthDate
}
dataview CustomerName {
Customer.firstName
Customer.lastName
}
108
Domain Model
repository CustomerDao for Customer {
operation Customer[] findAllLikeLastName(lastName):
from Customer customer where customer.lastName like :lastName
}
service CustomerService {
CustomerName CustomerDao.findAllLikeLastName
}
Note: you dont have to repeat the [] collection brackets in the overriden service operation
return type since its automatically derived from the delegating dao operation signature.
Service operations with support for runtime filter expressions
Service operations, which delegate to ql-based repository operations, support the
specification of some boolean flag (filter=true ) to enable the generation of an
additional method parameter. This parameter represents an expression (QueryObject)
wich is used to further restrict the existing ql WHERE clause with the specification of
additional criterias.
Note: this additional filter expression is always additive to the existing where clause and any
existing named parameter in the original ql statement has to be provided as well
The main target of this feature is the support of UI's with pageable und filterable table
requirements for which the user wants to add additional filter and sort criterias at runtime.
The following two steps are required to enable the usage of the above mentioned filter and
paging feature from the service layer.
1. define some arbitrary ql select statement
repository CustomerDao for Customer {
operation Customer[] findAllLikeLastName(lastName):
from Customer customer where customer.lastName like :lastName
}
2. first include the repository operation in some service in order make it available to clients
AND enable the generation of the additional filter parameter filter=true
service CustomerService {
CustomerDao.findAllLikeLastName
filter=true // this triggers the generation of an additional filter method
parameter
}
// this will generate the following two methods within the CustomerService
interface
Collection<CustomerView> findAllLikeLastName(String lastName);
Collection<CustomerView> findAllLikeLastName(String lastName,Expression _filter);
3. some manual client code using the expression filter to specify additional constraints
and customerim0_.FIRST_NAME=? order by customerim0_.LAST_NAME ascQueryObject
queryObject = new QueryObject();
Expression eqFirstName = queryObject.get("firstName").eq(customerEditDto.
getFirstName());
Expression lastNameAsc = queryObject.get("lastName").asc();
Collection<CustomerView> collection = customerService.
findAllLikeLastName(customerEditDto.getLastName(), queryObject.
where(eqFirstName).orderBy(lastNameAsc));
// creates the following pseudo sql (note the additional where predicate and
order by clause compared to the original ql statement in 1.)
select ... from CUSTOMER customerim0_
109
Domain Model
where
( customerim0_.LAST_NAME like ?
)
Note: if a filterable service operation (see above) is bound to some presentation model
component which supports paging and filtering, e.g. paging table and table customizer,
the manual declaration of the filter expression parameter above is not necessary since it
will be handled automatically by those components at runtime.
5.6.5 Generated Artefacts
For each service element one interface and one java class is generated.
Note: There is currently no built-in support to customize this behaviour without to change
the default provided workflow and templates.
110
Domain Model
Figure 5.15. Service artefacts
111
Domain Model
5.6.6 Syntax Diagram
112
Domain Model
5.6.7 Service Layer Infrastructure
The org.openxma.dsl.platform library supplies the service layer with
it's own set of generic and specialized exceptions - implemented in the
org.openxma.dsl.platform.excpetions package - which are supposed to
be the only exceptions a client of the service layer should deal with. For
resolution of the corresponding error messages you will find support in the
org.openxma.dsl.platform.i18n package.
Generic Exceptions
The platform library provides the generic SystemException for any technical problems
and the ApplicationException for custom business exceptions. They both derive from
the basic ErrorCodedException which is based on the idea of a generic exception class
that is used for all exceptional cases, communicating the error case by the help of an error
code.
In contrast to the XMA core at.spardat.enterprise.exc.SysException and
at.spardat.enterprise.exc.AppException (epclient library) the platform
exceptions:
• use a String error code instead of an int for better readability and ease of message
resolution
• don't transport the final error message but keep the given message parameters
• always require a default message which shall be used for exception logging
Specialized Application Exceptions and Helpers
There is a handful of ApplicationException children that cover special exceptional
cases:
• ConcurrentUpdateException for cases of concurrent updates
• NonUniqueException for violations of unique constraints
• BeanValidationException for violations of bean constraints, container of
BeanValidationException's, which will usually be a list of:
• PropertyValidationException for violations of bean property constraints,
derives from BeanValidationException
• ExceptionSupport supports the creation of validation exceptions from Spring
error objects
Validation with Spring Validators
Validation with Spring Validators is deprecated in favour JSR 303 Bean Validation as of
openXMA 6.0.0. For compatibility reasons it can still be used. See next chapter (Generator
Properties) how to configure the validation mechanism.
As of XMA version 4.1 automatic validation of data is done in the
XMA GUI only. The generated validators of the service layer extend
org.openxma.dsl.platform.validation.Validators and implement checks
for the constraint definitions of the entities' attributes. As a limitation they operate on the
generated entity classes only. Therefor the view objects (data transfer objects of the service
layer) have to be mapped to entities before they can be validated in the service methods.
Here as an example is the Book entity (Book.dml) of a library administration application:
import at.spardat.xma.types.*
entity Book {
113
Domain Model
id Long oid
version FipDate fipDate
CategoryEnum category
required=true
title="Category"
Integer(3) bookNumber
required = true
title = "Nr"
String(100) bookTitle
required = true
title = "Title"
String(100) subTitle
title = "Subtitle"
String(4) year
title = "Year"
LanguageEnum language
required = true
title = "Language"
unique categoryAndBookNumber(category, bookNumber)
Author[] authors
Lending[] lendings oppositeof book
}
Here you see the generated validator for the Book class, which checks for the 'required' and
the length constraints:
import
import
import
import
org.springframework.validation.Errors;
at.spardat.seabooks.book.model.Book;
org.openxma.dsl.platform.validation.Validators;
org.springframework.validation.ValidationUtils;
@SuppressWarnings("boxing")
public abstract class BookGenValidator extends Validators {
public boolean isCategoryRequired(Book book) {
return Boolean.TRUE;
}
public boolean isBookNumberRequired(Book book) {
return Boolean.TRUE;
}
public boolean isBookTitleRequired(Book book) {
return Boolean.TRUE;
}
public boolean isLanguageRequired(Book book) {
return Boolean.TRUE;
}
@SuppressWarnings("unchecked")
public boolean supports(Class clazz) {
return Book.class.isAssignableFrom(clazz);
}
public Errors validate(Book book) {
Errors errors = createErrors(book);
validate(book,errors);
return errors;
}
public void validate(Object object, Errors errors) {
Book book = (Book) object;
if (isCategoryRequired(book)) {
ValidationUtils.rejectIfEmpty(errors, "category", "field.required");
}
if (isBookNumberRequired(book)) {
ValidationUtils.rejectIfEmpty(errors, "bookNumber", "field.required");
}
if (isBookTitleRequired(book)) {
114
Domain Model
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "bookTitle", "field.
required");
}
if (isLanguageRequired(book)) {
ValidationUtils.rejectIfEmpty(errors, "language", "field.required");
}
rejectIfMaxIntegerDigits(errors,"bookNumber",book.getBookNumber(),3);
rejectIfMaxLength(errors,"bookTitle",book.getBookTitle(),100);
rejectIfMaxLength(errors,"subTitle",book.getSubTitle(),100);
rejectIfMaxLength(errors,"year",book.getYear(),4);
}
}
The Errors object returned from the validate method is a member of the Spring validation
framework.
Here is an example for the use of the validator in the update method of the BookDas service:
@Service("bookDas")
public class BookDasImpl extends BookDasGenImpl {
private BookValidator validator = new BookValidator();
@Transactional
public void update(BookView bookView) {
Assert.notNull(bookView, "parameter 'bookView' must not be null");
validate(bookView);
// ...do the update...
}
protected void validate(BookView bookView) {
// apply validators
Book book = this.mapper.createAndMapOne(bookView, Book.class,"saveBook");
Errors errors = this.validator.validate(book);
if (errors.hasErrors()) {
throw ExceptionSupport.mapToValidationException(errors, book);
}
}
// ...
}
Unique Constraints
The unique constraint shown in the Book entity does neither melt down to any constraint
check in the generated code nor is a unique constraint index in the DB schema derived from
it. The only artifact you will get from it is a load method in the DAO of the Book entity:
public interface BookDaoGen extends GenericDao<Book,Long>, EntityFactory<Book> {
// ...
Book loadBycategoryAndBookNumber(CategoryEnum category,Integer bookNumber);
}
The load method can be used in the Book service validation like this:
@Service("bookDas")
public class BookDasImpl extends BookDasGenImpl {
private BookValidator validator = new BookValidator();
@Transactional
public void update(BookView bookView) {
Assert.notNull(bookView, "parameter 'bookView' must not be null");
validate(bookView);
// ...do the update...
}
protected void checkConstraints(BookView bookView) {
115
Domain Model
// check unique constraints 'manually'
Book uniqueBook = this.bookDao.loadBycategoryAndBookNumber(bookView.
getCategory(), bookView.getBookNumber());
if (uniqueBook != null && ! uniqueBook.getOid().equals(bookView.getOid())) {
throw new NonUniqueException("Book", new String[] {"category",
"bookNumber"}, "Book with same category & bookNumber already exists");
}
}
protected void validate(BookView bookView) {
// apply validators
Book book = this.mapper.createAndMapOne(bookView, Book.class,"saveBook");
Errors errors = this.validator.validate(book);
if (errors.hasErrors()) {
throw ExceptionSupport.mapToValidationException(errors, book);
}
checkConstraints(bookView);
}
// ...
}
Error Message Resolution
The base exception class ErrorCodedException implements the interface
MessageResolvable which supplies all information necessary for the resolution of
messages. The error code of the exception is returned as key in order to be used for searching
resource bundles:
package org.openxma.dsl.platform.i18n;
public interface MessageResolvable {
public String getKey();
public Object[] getArguments();
public String getDefaultMessage();
}
The org.openxma.dsl.platform.i18n package furthermore provides the
interface MessageProvider that defines the methods for resolving a
bundle key or a MessageResolvable to the corresponding message. The
SimpleErrorMessageProvider is a base MessageProvider implementation that
allows for the resolution of error messages from platform exceptions. It can be given
application specific resource bundles, in case an error code (=bundle key) is not found there
the resource bundle org.openxma.dsl.platform.exceptions.ErrorMessage
will be searched for it as a default. There you will find the English messages for all the error
codes used by the DSL platform exceptions (validation errors, etc.). The default message,
which should always be available, will be returned in case the error code is not found in any
of the resource bundles.
Here
you
see
how
the
base
openXMA
DSL
serverside component org.openxma.dsl.platform.xma.SpringComponentServer
converts SystemException's and ApplicationException's to openXMA
core exceptions while injecting the error messages by the help of the
SimpleErrorMessageProvider.
public abstract class SpringComponentServer extends DslComponentServer {
protected ApplicationContext applicationContext;
protected MessageProvider messageProvider = new SimpleErrorMessageProvider();
//...
@Override
public Throwable convertToBaseException(Throwable detail) {
if (detail instanceof ApplicationException) {
116
Domain Model
return new AppException(detail, this.messageProvider.
getMessage((ApplicationException) detail,
getSession().getContext().getLocale()));
} else if (detail instanceof SystemException) {
return new SysException(detail, this.messageProvider.
getMessage((SystemException) detail,
getSession().getContext().getLocale()));
}
return super.convertToBaseException(detail);
}
//...
}
Note:Both exception types could be handled in one place as ErrorCodedException
but
we
preferred
to
treat
them
seperately.
Any
uncaught
Spring
exceptions that are returned from service methods will be converted to
at.spardat.enterprise.exc.SysException, which is the default processing of
openXMA RPC's.
5.7 DataType
DataType declaration start with the type keyword and are used to specify simple datatypes
whose details are not explicitly modeled and are used to
• implicitly or explicitly associated with a primitive type through their name (e.g.
String) or some mapping
• could be associated with a java instance type
• can redefine other DataType (e.g. default parameter values or additional parameter
declarations)
• extend the set of built-in datatypes available with the dsl platform library
(String,Integer,Boolean,Date,Long,..)
DataTypes can declare any number of string,int or boolean parameter to parameterize and
convey additional information. DataTypes are also identified by name, and they are used as
the types of attributes.
type BarType instancetype org.foo.BarType
The openXMA dsl platform ships with a default datatype library - Library.xmadsl - with predefined standard datatypes like String,Integer,Long,Float,Boolean,Date,Timestamp,Double
and BigDecimal. The datatypes within this library are implicitly visible to all referring
elements and don't have to be manually imported. Some of this datatypes are parameterizable
and therefore accept one ore more parameter values to set some additional constraints on the
corresponding attribute type.
entity Customer extends BaseEntity {
String(25) firstName
Date("Full") birthDate
}
For example the String(25) declaration taken from the previous example states that the
length of the text value of the corresponding attribute is restricted to a maximum of 25
characters.
5.8 Enum
Like DataTypes, enumeration can be defined inside files with the .xmadsl extension. When
an enum attribute is referenced in the presentation model, combo boxes are used by default.
117
Domain Model
Enum declarations start with the enum keyword followed by the name of the enum and are
used to combine a certain set of values under a given name. Like datatypes enums have to
be declared within a dsl file ending with the xmadsl extension and from there on can be
referenced by domain model elements with their (qualified) name.
enum Gender {
MALE("M")
FEMALE("F")
}
.
5.9 Bean Validation
Introduced in openXMA 6.0.0, there is support for Bean Validation (JSR 303) to validate
entities and data views (data transfer objects).
Here as an example is the Book entity (Book.dml) of a library administration application:
entity Book {
id Long oid
version FipDate fipDate
CategoryEnum category
required=true
title="Category"
Integer(3) bookNumber
required = true
title = "Nr"
String(100) bookTitle
required = true
title = "Title"
String(100) subTitle
title = "Subtitle"
String(4) year
title = "Year"
LanguageEnum language
required = true
title = "Language"
Topic topic
title = "Topic"
unique categoryAndBookNumber(category, bookNumber)
Author[] authors
Lending[] lendings oppositeof book
}
dataview BookView{
Book.*
}
On generation, the respective entity and data views are enhanced with constraint definitions
according to the domain model. The following code examples show the generated code for
an entity, but also applies for data views. You can see the constraints on field level which
correspond to the restrictions in the domain model. Every constraint is given a unique error
message code which allows for specific customization of error messages, see Error message
mesolution.
package at.spardat.seabooks.book.model.impl;
import ...
@ValidBook
public abstract class BookGenImpl implements Book {
protected Long oid;
protected Date fipDate;
@NotNull(message="{validation.NotNull.CategoryEnum.Book.category}")
118
Domain Model
protected CategoryEnum category;
@NotNull(message="{validation.NotNull.Integer.Book.bookNumber}")
@Digits(integer=3,fraction=0,message="{validation.Digits.Integer.Book.
bookNumber}")
protected Integer bookNumber;
@NotEmpty(message="{validation.NotEmpty.String.Book.bookTitle}")
@Size(max=100,message="{validation.Size.String.Book.bookTitle}")
protected String bookTitle;
@Size(max=100,message="{validation.Size.String.Book.subTitle}")
protected String subTitle;
@Size(max=4,message="{validation.Size.String.Book.year}")
protected String year;
@NotNull(message="{validation.NotNull.LanguageEnum.Book.language}")
protected LanguageEnum language;
protected Topic topic;
protected Set<Author> authors;
protected Set<Lending> lendings;
Constraints on fields can't be disabled, but additional constraints can be added in the derived
class by annotating getter methods of the fields. See below how to put another limitation on
a field:
package at.spardat.seabooks.book.model.impl;
import javax.validation.constraints.Pattern;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component("bookPrototype")
@Scope("prototype")
public class BookImpl extends BookGenImpl {
@Pattern(regexp = "[0-9]{4}")
public String getYear() {
return super.getYear();
}
}
You can also see a class level constraint (@ValidBook) on BookGenImpl which allows
for validation of custom checks, for example cross field checks. This annotation class is also
generated and looks like this:
package at.spardat.seabooks.book.validation;
import ...
@Target({ TYPE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { BookValidator.class })
public @interface ValidBook {
String message() default "{at.spardat.seabooks.book.validation.ValidBook.
message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
The
@Constraint
annotation
refers
to
the
implementation
class
(validateBy={BookValidator.class}) where you can override the validation
method to do some more exotic checks, e.g.:
package at.spardat.seabooks.book.validation;
import ...
119
Domain Model
public class BookValidator extends BookGenValidator {
@Override
public boolean isValid(Book book, ConstraintValidatorContext context) {
if (book != null && CategoryEnum.O.equals(book.getCategory()) && book.
getAuthors().size() <= 1) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
"{at.spardat.seabooks.book.validation.Book.AuthorsSize.
message)").addConstraintViolation();
return false;
}
return super.isValid(book, context);
}
}
5.9.1 Validating an object
To validate an object we make use of Spring's support for bean validation and configure a
validator in our applicationContext.xml. This has the advantage that we can pass multiple
resource bundles for error message resolution. Note that in the following configuration we
have already configured message code resolution support as described in the next section.
<bean id="validatorResourceBundle" class="org.openxma.dsl.platform.validation.
Jsr303ValidationMessageSource">
<property name="basenames">
<list>
<value>at.spardat.seabooks.book.BookCustomMessages</value>
</list>
</property>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.
LocalValidatorFactoryBean" >
<property name="validationMessageSource" ref="validatorResourceBundle" />
</bean>
Here is an example for the use of validation in a service layer implementation. In this case
we work on a data view object.
@Service("bookDas")
public class BookDasImpl extends BookDasGenImpl {
@Autowired
private Validator validator;
@Transactional
public void update(BookView bookView) {
Assert.notNull(bookView, "parameter 'bookView' must not be null");
validate(bookView);
// do the update
}
protected void validate(BookView bookView) {
// apply validators
Set<ConstraintViolation<BookView>> violations = validator.
validate(bookView);
if (!violations.isEmpty()) {
throw mapToValidationException(violations, bookView);
}
checkConstraints(bookView);
}
protected void checkConstraints(BookView bookView) {
// check unique constraints 'manually'
Book uniqueBook = this.bookDao.loadBycategoryAndBookNumber(bookView.
getCategory(), bookView.getBookNumber());
if (uniqueBook != null && ! uniqueBook.getOid().equals(bookView.getOid())) {
throw new NonUniqueException("Book", new String[] {"category",
"bookNumber"}, "Book with same category & bookNumber already exists");
}
120
Domain Model
}
// ...
}
5.9.2 Error message resolution
Every generated constraint contains a message code that is specific to the violation.
This allows to override validation error messages specificly for every constraint. On
validation of an object, error codes are being resolved using the message bundles
configured in the Spring application context as shown in the previous section.
Additionally to the default JSR 303 message resolution, the message source provider
org.openxma.dsl.platform.validation.Jsr303ValidationMessageSource
enriches the resolution in the following way:
For every message code to be resolved, the provider tries to resolve the most
specific code available, falling back to more generic codes. Given the message code
validation.Size.String.Book.bookTitle the following codes are looked up in
the configured resource bundles in the given order. If lookup fails, the subsequent code is
used:
•
•
•
•
validation.Size.String.Book.bookTitle
validation.Size.String.Book
validation.Size.String
validation.Size
At this point, resolution will succeed, because the code validation.Size is provided
as a default message.
Default messages
The
message
source
provider
org.openxma.dsl.platform.validation.Jsr303ValidationMessageSource
looks up codes in all configured resource bundles, and additionally supports two bundles for
default resolution.
The
property
bundle
org.openxma.dsl.platform.validation.XMAValidationMessages.properties
maps default XMA validation codes to standard JSR 303 message codes, which are included
in the underlying validation implementation. It is not subject to localization:
validation.NotNull={javax.validation.constraints.NotNull.message}
validation.Size={javax.validation.constraints.Size.message}
validation.Digits={javax.validation.constraints.Digits.message}
validation.DecimalMin={javax.validation.constraints.DecimalMin.message}
validation.DecimalMax={javax.validation.constraints.DecimalMax.message}
validation.NotEmpty={org.openxma.dsl.platform.validation.constraints.NotEmpty.
message}
validation.ValidFormat={org.openxma.dsl.platform.validation.constraints.ValidFormat.
message}
The
second
bundle,
org.openxma.dsl.platform.validation.DefaultValidationMessages.properties,
gives default error messages for XMA specific constraints and overrides standard messages
provided by the JSR 303 which don't have an appropriate wording in the supplied messages
of the underlying validation implementation:
validation.Size.String=must have a length between {min} and {max} characters
org.openxma.dsl.platform.validation.constraints.NotEmpty.message=may not be empty
121
Domain Model
org.openxma.dsl.platform.validation.constraints.ValidFormat.message=has an invalid
format
Note,
that
you
should
provide
a
translated
version
of
DefaultValidationMessages.properties in your classpath if no suitable file for
your Locale is included in the platform jar.
122
Chapter 6: Generator
This chapter describes the available possibilities used to customize and interact with the openXMA
generator.
6.1 Properties
Within eclipse generator properties are easily configured by the means of standard eclipse
preference & project properties as shown in the next figure.
123
Generator
• Generated Java Directory
124
Generator
the directory used for generated java source code
• Generated Resources Directory
specifies the directory containing generated resources like properties or xml files
• Documentation Directory
FFU
• Naming Strategy
the fully qualified class name of the strategy implementation which is used to derive
and calculate names (e.g. column and table names)
• Layout Strategy
the fully qualified class name of the strategy implementation which is used to
determine various layout properties, like the width of widgets.
• ModelModifier Strategy
ATM only for internal use
• Xpand Aspect Files
project relative path to an Xpand file which can be used to override or replace the
default generator templates (see next section)
• Additional MWE Workflow Files
this section is meant for projects which want to use openXMA models but do not
want to use the default generator or do need to produce additional content besides the
default templates. The value is interpreted as project relative path to a custom MWE
file as described in the next sections.
6.1.1 Additional Optional Features
Now we can control the resources the openXMA generator takes to generate the required
resources for us. For this we can specify the semicolon separated list of packages and/or files
to be included in the generated process in the generator.properties file. For example:
domainModel.includePackagesFilter=org.openxma.demo.a
will tell the generator to include all the dml's within the org.openxma.demo.a package and
recursively using the sub-directories as well. Similarly, you can add more than one as per
your need (';' separated)
domainModel.includePackagesFilter=org.openxma.demo.a;org.openxma.demo.b
Similarly, for the presentation model it can be done as:
presentationModel.includePackagesFilter=org.openxma.demo.a
By doing this we explicitly tell opneXMA generator to only include all the pml's in this
package and all the pml's in it's sub-packages recursively for the generation process.
6.2 Templates
If you want to use the default generator but need to override the templates to generate some
additional content, you can make use of the AROUND aspects feature of Xpand1 to achieve
this goal. The following section lists the currently defined Xpand template hooks together
with a brief description of the generated content.
For example to add some arbitrary custom annotations to every attribute contained within an
entity could be accomplished with the following around advice.
«EXTENSION extensions::Names»
1
http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.xpand.doc/help/core_reference.html
125
Generator
/**
* adds the annotation 'FooAnnotation' to every entity attribute
*/
«AROUND *::attribute FOR dom::Attribute»
«IF dom::Entity.isInstance(eContainer)-»
@FooAnnotation
private «getImplementationType()» «name-» «IF dom::ValueObject.isInstance(type.
dataType)-» = new «getImplementationType()-»()«ENDIF-»;
«ELSE-»
«targetDef.proceed()»
«ENDIF-»
«ENDAROUND»
• DomainModel::main(Model)
Defines the main entry point template for the DOM generator.
• Entity::main(Model)
Defines the main template for Entities and invokes the following templates. Templates
named genInterface,manInterface,genClass and manClass are used to create the
generated and manual parts of the Interface and Implementation artefacts as described
previously in the generation gap pattern section.
• Entity::genInterface(Entity)
• Entity::manInterface(Entity)
• Entity::genClass(Entity)
• Entity::manClass(Entity)
• Entity::genOperationInterface(Entity) - additional entity
operations within the generated interface
• Entity::genOperationImpl(Entity) - additional entity operations
within the generated implementation
• Entity::manOperationInterface(Entity) - additional entity
operations within the manual interface
• Entity::manOperationImpl(Entity) - additional entity operations
within the manual implementation
• Entity::toString(Entity)
• Entity::equalsHashCode(Entity)
• Entity::compositeIdClass(Entity) - composite identifiers for
entities with composite keys (only)
• Repository::main(Model)
Defines the main template for Repositories and invokes the following templates
• Repository::genInterface(Dao)
• Repository::manInterface(Dao)
• Repository::genClass(Dao)
• Repository::manClass(Dao)
• Repository::genImportsImpl(Dao) - add additional imports in the
generated implementation
• Repository::manImportsImpl(Dao)- add additional imports in the
manual implementation
• Repository::createOperationInterface(Dao) - create operations
interface
• Repository::createOperationImpl(Dao) - create operations
implementation
126
Generator
• Repository::genOperationInterface(Dao) - additional repository
operations within the generated interface
• Repository::genOperationImpl(Dao)additional
repository
operation implementations in the generated implementation
• Repository::manOperationInterface(Dao) - additional repository
operations within the manual interface (only created if not already exists)
• Repository::manOperationImpl(Dao)additional
repository
operation implementations in the manual implementation (only created if not
already exists)
• Repository::dbConstraintFinderSignature(DataBaseConstraint)
• Repository::dbConstraintFinderImpl(DataBaseConstraint)
• Repository::daoOperationSignature(Operation)
• Repository::daoOperationImpl(Operation)
• Repository::queryOperationSignature(QueryOperation)
• Repository::queryOperationImpl(QueryOperation)
• Service::main(Model)
Defines the main template for Services and invokes the following templates
• Service::genInterface(Service)
• Service::manInterface(Service)
• Service::genClass(Service)
• Service::manClass(Service)
• Service::manImportsImpl(Service)- add additional imports in the
manual implementation
• Service::manImportsImpl(Service)- add additional imports in the
manual implementation
• Service::genOperationInterface(Service) - additional service
operations within the generated interface
• Service::genOperationImpl(Service)- additional service operation
implementations in the generated implementation
• Service::manOperationInterface(Service) - additional service
operations within the manual interface (only created if not already exists)
• Service::manOperationImpl(Service)- additional service operation
implementations in the manual implementation (only created if not already exists)
• Service::genInterfaceAnnotation(Service)- additional service
annotations within the generated service interface
• DataView::main(Model)
Defines the main template for DataViews and invokes the following templates
• DataView::genClass(DataView)
• DataView::manClass(DataView)
• Validator::main(Model)
Defines the main template for Validators and invokes the following templates
• DataView::genValidatorClass(Entity)
• DataView::manValidatorClass(Entity)
• ValueObject::main(Model)
Defines the main template for ValueObjects and invokes the following templates
127
Generator
• ValueObject::genClass(ValueObject)
• ValueObject::manClass(ValueObject)
• Context::main(Model)
Defines the main template for ValueObjects and invokes the following templates
• Context::genInterface(ApplicationSession)
• Context::manInterface(ApplicationSession)
• Context::genClass(ApplicationSession)
• Context::manClass(ApplicationSession)
• Features
Various templates for the generation of common ComplexType
(Entity,DataView,ValueObject) features like attributes, properties, toString and
serialVersionUID.
• Features::serialVersionUID(ComplexType)
default
serialVersionUID as XOR hash from the attribute names of the given
ComplexType
• Features::toString(ComplexType) - default toString for a
ComplexType
• Features::attributes(ComplexType) - generates variable declaration
for all attributes for the given ComplexType
• Features::attribute(Attribute) - generates a variable declaration
for the given Attribute
• Features::properties(Attribute) - generates setter and getters
implementations for the given Attribute
• Features::accessor(Attribute) - getter signature for the given
attribute
• Features::mutator(Attribute) - setter signature for the given attribute
• Features::properties(Property) - configuration code for static
property values
• Features::operationAnnotation(Operation,isImplementation)
- additional annotations for interface and implementation operations
• Features::operationAnnotation(DelegateOperation,isImplementation)
- additional annotations for interface and implementation operations delegating to
repositories
• Features::parameterAnnotation(Parameter,isImplementation)
- additional annotations for parameters on interface and implementation
operations
6.3 Output configuration
Output configuration is done through the specification of so-called Xpand Outlets. Outlets
are responsible for writing the generated files to disk. The following Outlets are available.
• OUTLET_JAVA
directory containing manual java sources (files within this directory are never
overriden once created)
• OUTLET_TEST
128
Generator
directory containing manual test java sources (files within this directory are never
overriden once created)
• OUTLET_RESOURCES
directory containing manual resources like properties and xml files (files within this
directory are never overriden once created)
• OUTLET_GENERATED_JAVA
directory containing generated java sources (files within the 'generated' directories are
getting overridden on every generator run)
• OUTLET_GENERATED_TEST
directory containing generated test sources
• OUTLET_GENERATED_RESOURCES
directory for generated resource files like properties..
6.4 NamingStrategy
NamingStrategy implementation are responsible to derive default names of generated
Artefacts (e.g column and table names), given the respective model element.
/**
* Strategy interface for determining various names, like column and table
* names, given the respective model elements. This interface is used from the
* default workflow and templates during a generator run.
*
* Implementations use this to implement project-scoped naming standards.
*/
public interface NamingStrategy {
/**
* Return a table name for an {@code Dao}
*
* @param dao
*
the dao instance to generate the table name for
* @return a table name for the given dao
*/
String getTableName(Dao dao);
/**
* Return the alias name for an {@code Dao}.
*
* @param dao the dao instance to generate the table alias for
* @return an alias name for the given dao
*/
String getQualifier(Dao dao);
/**
* Return the column name for an <code>Attribute</code>.
*
* @param entity the entity of the given attribute
* @param feature the feature to calculate the column for
* @return a column name for the given feature
*/
String getColumnName(Entity entity, Attribute feature);
/**
* Return the column name for a nested (embeddable) <code>Attribute</code>.
*
* @param entity the entity of the given attribute
* @param feature the feature to calculate the column for
* @param nestedfeature
* @return a column name for the given nested feature
*/
String getColumnName(Entity entity, Attribute feature, Attribute nestedfeature);
129
Generator
/**
* Return the implementation package name for the given modeElement.
*
* @param modeElement
*
the modeElement instance to calculate the package for
* @return the implementation package name for the given modeElement
*/
String getPackageName(ModelElement modeElement);
/**
* Return the interface package name for the given modeElement.
*
* @param modeElement
*
the modeElement instance to calculate the package for
* @return the interface package name for the given modeElement
*/
String getInterfacePackageName(ModelElement modeElement);
}
Projects can implement this interface or extend from the built-in
DefaultNamingStrategy to enforce project-specific naming standards. The
generator determines the current NamingStragy based on the value of the
domainModel.namingstrategy.class property. Within eclipse a specific
NamingStrategy is easily configured by the means of standard eclipse project properties as
already described in the previous section (see Section 6.1, “Properties”). The following listing
gives an example for such a project specific NamingStrategy implementation and shows the
customized generator output.
public class ProjectNamingStrategy extends DefaultNamingStrategy {
@Override
public String getTableName(Dao dao) {
return String.format("T_%s", dao.getEntity().getName().toUpperCase());
}
@Override
public String getColumnName(Entity entity, Attribute attribute) {
String entityName = entity.getName().toUpperCase();
String attributeTypeName = attribute.getType().getDataType().getName();
String attributeName = attribute.getName().toUpperCase();
return String.format("C_%S_%s_%s", attributeTypeName, entityName, attributeName);
}
@Override
public String getColumnName(Entity entity, Attribute feature, Attribute
nestedFeature) {
String embeddableName = feature.getName().toUpperCase();
String attributeName = nestedFeature.getName().toUpperCase();
return String.format("C_%s_%s", embeddableName, attributeName);
}
}
Note: you'll have to include the following additional jars: dsl-core-version.jar, dsl-domversion.jar and emfs ecore and common jars.
Based on the example Customer demo model and the previous custom naming strategy the
generator would create the following column and table names.
<class name="org.openxma.demo.customer.model.impl.CustomerImpl"
table="T_CUSTOMER" discriminator-value="CSTMR">
<id name="oid" access="property">
<column name="C_STRING_CUSTOMER_OID" >
<comment><![CDATA[technical identifier of CustomerDao]]></comment>
</column>
<generator class="assigned" />
</id>
<version name="version" access="field" unsaved-value="null"
type="timestamp">
130
Generator
<column name="C_DATE_CUSTOMER_VERSION"/>
</version>
<property name="firstName" not-null="true">
<column name="C_STRING_CUSTOMER_FIRSTNAME"/>
</property>
<property name="lastName" not-null="true">
<column name="C_STRING_CUSTOMER_LASTNAME"/>
</property>
<property name="emailAddress" not-null="true">
<column name="C_STRING_CUSTOMER_EMAILADDRESS"/>
</property>
<property name="birthDate" not-null="true">
<column name="C_DATE_CUSTOMER_BIRTHDATE"/>
</property>
<component name="invoiceAddress" class="org.openxma.demo.customer.Address"
access="field">
<property name="streetName">
<column name="C_INVOICEADDRESS_STREETNAME" />
</property>
<property name="streetNumber">
<column name="C_INVOICEADDRESS_STREETNUMBER" />
</property>
<property name="zip">
<column name="C_INVOICEADDRESS_ZIP" />
</property>
<property name="city">
<column name="C_INVOICEADDRESS_CITY" />
</property>
<property name="country">
<column name="C_INVOICEADDRESS_COUNTRY" />
</property>
</component>
<component name="deliveryAddress" class="org.openxma.demo.customer.Address"
access="field">
<property name="streetName">
<column name="C_DELIVERYADDRESS_STREETNAME" />
</property>
<property name="streetNumber">
<column name="C_DELIVERYADDRESS_STREETNUMBER" />
</property>
<property name="zip">
<column name="C_DELIVERYADDRESS_ZIP" />
</property>
<property name="city">
<column name="C_DELIVERYADDRESS_CITY" />
</property>
<property name="country">
<column name="C_DELIVERYADDRESS_COUNTRY" />
</property>
</component>
</class>
6.5 ModelModifier
If a project needs more control over the generated artefacts it is possible to implement a so
called ModelModifier to change existing parts of a model or to create additional synthetic
elements. ModelModifier is callback interface or pre-processor used to enhance the given
model instance right before it is processed by the generator templates and after any validation
checks. Like the c a ModelModifier is configured by means of eclipse project properties
(node openXMA).
public interface ModelModifier {
/**
* Callback method to augment the given <code>Model</code> instance.
* @param model the model to augment
*/
void modifyModel(Model model);
}
131
Generator
6.6 Hibernate Properties
Hibernate properties are used to customize the generation of the hibernate mapping. Up to
now the following hibernate settings are configurable:
• UserType for built-in DataTypes (ValueTypes) e.g. to override the default boolean
type with org.hibernate.type.TrueFalseType or org.hibernate.type.YesNoType
• SqlType allows the user to override the default mapping of a Hibernate type to SQL
datatype. (e.g. override default TIMESTAMP mapping for date types with DATE sql
type)
• In certain cases you will need the (property)type attribute. For example, to distinguish
between Hibernate.DATE (i.e. date) and Hibernate.TIMESTAMP (i.e. timestamp).
6.7 Service Layer Properties
Also, the Base class for generated service classes can be specified. The default is empty.
132
Generator
133
Generator
6.8 Starting with Maven
Maven integration is provided with a openXMA generator mojo which can be used to run
the domain model generator standalone, outside of eclipse which is required for build/CI
scenarios.
<plugin>
<groupId>org.codehaus.openxma</groupId>
<artifactId>dsl-generator-mojo</artifactId>
<version>3.6.2-SNAPSHOT</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate-dom</goal>
</goals>
</execution>
</executions>
<configuration>
<properties>${basedir}/src/main/resources/project_generator.properties</
properties>
</configuration>
</plugin>
MOJO Documentation
Full name: org.codehaus.openxma:dsl-generator-mojo$VERSION$:generate-dom
Description: Maven plugin used to execute the openXMA Dom generator workflow
• Requires a Maven 2.0 project to be executed.
• Requires dependency resolution of artifacts in scope: compile.
• Binds by default to the lifecycle phase: generate-sources.
Name
Required
Description
Default value
workflowFile
yes
The location of the
workflow descriptor.
workflow/
DomGenerator.mwe
generatedResourcesOutlet no
Path to the directory
containing manual
resources like properties
and xml files
${basedir}/src/generated/
resources.
generatedSourceOutlet
Path to the directory
${basedir}/src/generated/
containing generated java java.
source files
no
generatedTestResourcesOutlet
no
Path to the direcotry
containing generated
resources
generatedTestSourceOutletno
Path to the directory
${basedir}/src/test/
containing generated test generated/java.
java source files
properties
no
The location of the
generator properties file.
n/a
modelFolder
no
Path to the folder
containing openXMA
model files
${basedir}/src/main/
model.
resourcesOutlet
no
Path to the directory
containing manual
resource file
${basedir}/src/main/
resources.
templateAdviceFiles
no
The location of the
custom template advice
file.
n/a
testResourcesOutlet
no
Path to the directory
${basedir}/src/test/
containing test resources. resources.
134
${basedir}/src/test/
generated/resources.
Generator
Name
Required
Description
Default value
testSourceOutlet
no
Path to the directory
containing test sources.
${project.build.testSourceDirectory}.
6.9 Starting from eclipse
The generator is fully integrated with eclipse an is executable from within an opened dsl
editor or by selecting particular dsl files and packages from the package explorer view as
outlined in the next figure.
135
Generator
136
Generator
or as batch from the package explorer
137
Generator
6.10 Generator Patterns
The default generator templates are following an interface–implementation separation which
is a well known pattern established by many frameworks which allows you to have multiple
implementations for your interface. Additional to interface-implementation separation the
generator follows the Generation Gap pattern. The intent of this pattern is to modify or extend
generated code just once no matter how many times it is regenerated.
6.11 Workflow
This is the workflow file which is used by default from the maven generator mojo and also
from the generated openXmaDsl sample archetype project. The sequence of activities in the
default workflow is shown in the following figure
138
Generator
Figure 6.1. Domain model generator workflow
<workflow>
<property
<property
<property
<property
<property
<property
name="outlet.java" value="src/main/java"/>
name="outlet.test" value="src/test/java"/>
name="outlet.resources" value="src/main/resources"/>
name="outlet.test.resources" value="src/test/resources"/>
name="outlet.generated.java" value="src/generated/java"/>
name="outlet.generated.test" value="src/test/generated/java"/>
139
Generator
<property name="outlet.generated.resources" value="src/generated/resources"/>
<property name="outlet.generated.test.resources" value="src/test/generated/
resources"/>
<property name="modelPackage"/>
<property name="checkFile"/>
<property name="templateAdviceFiles"/>
<property name="extensionAdviceFiles"/>
<bean class="org.eclipse.mwe.emf.StandaloneSetup" platformUri="..">
<registerGeneratedEPackage value="org.openxma.xmadsl.model.XmadslPackage"/>
</bean>
<component class="org.openxma.dsl.generator.component.DomainModelParserComponent">
<modelPackage value="${modelPackage}"/>
<outputSlot value="domainModel"/>
</component>
<component class="oaw.check.CheckComponent">
<metaModel id="metamodel" class="org.eclipse.m2t.type.emf.EmfRegistryMetaModel"
useSingleGlobalResourceSet="true"/>
<checkFile value="org/openxma/xmadsl/Checks"/>
<emfAllChildrenSlot value="domainModel"/>
</component>
<if cond="${doCustomChecks}">
<component class="oaw.check.CheckComponent">
<metaModel idRef="metamodel"/>
<checkFile value="${checkFile}"/>
<emfAllChildrenSlot value="domainModel"/>
</component>
</if>
<component class="org.openxma.dsl.generator.component.
DomainModelModifierComponent">
<modelSlot value="domainModel"/>
</component>
<component id="generator" class="oaw.xpand2.Generator" skipOnErrors="true"
fileEncoding="iso-8859-1">
<metaModel idRef="metamodel"/>
<outlet path="${outlet.generated.java}" />
<outlet name="OUTLET_JAVA" path="${outlet.java}" overwrite="false"/>
<outlet name="OUTLET_TEST" path="${outlet.test}" overwrite="false" />
<outlet name="OUTLET_RESOURCES" path="${outlet.resources}" overwrite="false" />
<outlet name="OUTLET_TEST_RESOURCE" path="${outlet.test.resources}"
overwrite="false" />
<outlet name="OUTLET_GENERATED_TEST" path="${outlet.generated.test}"
overwrite="true" />
<outlet name="OUTLET_GENERATED_RESOURCE" path="${outlet.generated.resources}"
overwrite="true" />
<outlet name="OUTLET_GENERATED_RESOURCES" path="${outlet.generated.resources}"
overwrite="true" />
<outlet name="OUTLET_GENERATED_TEST_RESOURCES" path="${outlet.generated.test.
resources}" overwrite="true" />
<expand value="templates::DomainModel::main FOR domainModel"/>
</component>
<component adviceTarget="generator" id="generatorAdvice" class="org.openxma.dsl.
generator.component.GeneratorAdviceComponent">
<advice value="${templateAdviceFiles}" />
<extensionAdvice value="${extensionAdviceFiles}" />
<fileEncoding value="iso-8859-1" />
</component>
</workflow>
140
Chapter 7: Tooling
This chapter describes the built-in tooling features to support the work with openXMA model files.
7.1 Ddl Import Wizard
The Ddl Import Wizard can be useful if you need to reverse engineer some domain model
files from an existing database schema. Select a file containing ddl statements and choose
from the tables (and columns) you want to import as shown in the next figure.
141
Tooling
Note: Since hibernate already provides an automatic schema generation toolset1 we don't
provide additional tools to create ddl scripts from domain model files.
1
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/toolsetguide.html
142
Tooling
7.2 Navigation
7.3 Views
7.4 Diagrams
143
Chapter 8: Customization
This chapter describes the manifold possibilities to customize XMAdsl.
8.1 Spring Configuration: Autowire vs. XML beans
All major artefacts like services and repositories are made available as spring beans with
injected dependencies.
By default, spring beans are created via component scan: (applicatoinContext.xml)
<!-- autodetect and register 'stereotyped' classes such as services and
repositories -->
<context:component-scan base-package="org.openxma.dsl.reference"/>
In scenarios with a large classpath, this can lead to long start-up times. As a solution you can
include generated xml beans instead of this component scan.
<!-- import generated services and repositories -->
<import resource="classpath:applicationContext-applicationBeans.xml"/>
If you do so, you have make sure that also the manually created components are made
available in the context.
8.2 Using XProperties
The XProperties system (Section 1, “Introduction” in XProperties Tutorial) offers
enhancements to the standard Java properties as:
•
•
•
•
•
•
•
Include mechanism to load multiple files in one application property set
Prefixes to shorten the key text
References in values to other properties
Conditional value setting (often used for different environments specific settings)
Timer driven value reloading
Enumerated groups of keys
Reading properties form databases
8.2.1 Using XProperties in Spring's application context
If you want to use to XProperties in applicationContext.xml, you can simply activate a Spring
property placeholder configurer:
<bean class="org.openxma.dsl.platform.config.
XPropertiesPlaceholderConfigurer" />
Then, you can refer to XProperty entries with Expression Language syntax. The following
sets the property myProperty of the instance of MyTestingBean to the value of the
at.spardat.sample.hello property.
<bean class="org.openxma.demo.customer.MyTestingBean" p:myProperty="${at.
spardat.sample.hello}" />
144
Customization
This mechanism can be used e.g. to externalize DB passwords from applicationContext.xml
file.
145
Chapter 9: Paging and customizing
Tables
This chapter describes the behaviour of the PagingControl and the oppertunity to customize Tables using
the Tablecustomizer in an OpenXMA Dsl project.
9.1 PagingControl
9.1.1 General Description
The PagingControl addresses the issue of displaying large tables in an efficient and user
friendly manner. The UI is provided with the following navigation controls - listed in the
order as they appear in the UI from left to right - see also the sample screenshot:
• First: navigates back to the first page. Deactivated if page is already the first page.
• Fastback: navigates jumpSize pages back. If less pages than jumpSize exist, the first
page is shown. The Button is deactivated on the first page.
• Back (Previous) Navigates one page back if the current page is not the first page.
• Reload: triggers a reload event on the pagingControl with the current offset
• Next: Navigates one page forward.
• FastForward: navigates jumpSize pages forward. If less pages than jumpSize exist, the
last page is shown. The Button is deactivated on the last page.
• Last: Jumps to the last page. Deactivated if the total Resultsize ist unknown.
• Pagesize (Combo): User can define the page size by changing the value of this combo.
The page ize is the row count that is displayed at once in the table.
Figure 9.1. Sample figure of table using the PagingControl
9.1.2 Modeling the PagingControl
To use the PagingControl in a page means to define the table in your pml-model as pageable:
First you define the table that you want to use with the PagingControl with the attribute
pageable=true and a style attribute with a valid name like style=myPagingStyle.
table customerTable key=customers.oid pageable=true style=myPagingStyle
top=customerTableHeader bottom=100% {
customers.firstName
width=80
146
Paging and customizing Tables
customers.lastName
width=140
customers.birthDate
width=140
customers.emailAddress width=140
}
Next, you define the style (myPagingStyle in this example) where you choose which
navigation elements to be visible on the control. In this example we want to show all
navigation elements:
style myPagingStyle {
paging-position : top
paging-style : custom
paging-jump-size : 10
paging-page-size : 10 max = 1000
paging-controls : back, customize, pagesize, next, fastnext, fastback, info,
reload, start, end
}
Assuming you intend to display some data in your table, meaning loading data initially when
the page becomes first visible, you can do this in the eventmapping block:
command loadData
eventmapping {
onEnter -> loadData
}
This instructs the generator to generate code that calls a loadData remoteCall when the page
becomes visible.
Additionally you need a place where to react on reload Events which are triggered from the
UI navigation elements of the paging control:
command loadData
eventmapping {
onEnter -> loadData
customerTable.onLoad -> loadData
}
The above statement (customerTable.onLoad -> loadData) does exactly this. Every UI Event
triggered from the pagingControl navigation elements results in a remote call to "loadData".
So in this example an abstract method loadData is generated on the server side and has to
be implemented to handle the backend logic.
This is all you have to do for displaying the pagingControl on your table. If you generate this
pml you find on the client side the following generated code:
public void createModels(
...
customerTable_pagingControl = new PagingWMClient((short) 4,this,customerTable)
customerTable_pagingControl.setPageSize((short)10);
customerTable_pagingControl.setJumpSize((short)10);
customerTable_pagingControl.setPagingListener(new PagingListener() {
public void reload(int offset, int pagesize) {
loadData(null);
}
});
...
protected void loadData(SelectionEvent event) {
newRemoteCall("loadData").execute();
}
...
147
Paging and customizing Tables
The loadData Implementation on the server side could look like this
@Override
public void loadData(RemoteCall call, RemoteReply reply) {
int offset = customerTable_pagingControl.getOffset();
short pageSize = customerTable_pagingControl.getPageSize();
short sortingColumn = customerTable_pagingControl.getSortingColumn();
boolean isAscending = customerTable_pagingControl.getAscending();
// fetch your data according to these paging control parameters
...
}
9.1.3 TableCustomizer
In Conjunction with the PagingControl you can optionally also use the TableCustomizer
that addresses the following issues:
•
•
•
•
allow user to define which columns of the table are visible
allow user to define the sort order and sort direction of the table data
allow user to define the order in which the columns appear in the table
allow user to define filters for columns of type Numbers, Strings, Dates,
I18Enumeration Domains
• allow user to persist their settings
Figure 9.2. Sample Screenshot of the TableCustomizer dialog
In the figure above the dialog shows the following settings:
• all columns are visible
• there is a filter set on the "First name" column
148
Paging and customizing Tables
• "First name" appears as the third column in the table (seen from left to right)
• the table rows are sorted first ascending by last name, then descending by first name
Filters are set by the filters dialog. For each column of a valid type (as listed above), the user
can define a filter. A filter on a column is an expression. A filter expression can be either
made up of AND-subexpressions or OR-subexpressions. If there exist more than one column
with an filter, the filter expressions are joined with the AND operator.
Example. The user wants to a list of persons whose first name is equal to ("John" or "Frank")
AND whose birthdate is before 1.1.1985:
The user can also choose to allow NULL rows to be valid.
All this can also be modeld in dsl. There is a little more information needed in your .pml file.
To enhance our previous example with the tableCustomizer you would have to update the
above modeling file in the following way:
style pagingTable {
table-customizer : testTableCustomizer
paging-customizer-image : "/org/openxma/demo/customer/xma/client/cog_go.png"
paging-position : top
paging-style : custom
paging-jump-size : 10
paging-page-size : 10 max = 1000
paging-controls : back, customize, pagesize, next, fastnext, fastback, info,
reload, start, end
}
tablecustomizer testTableCustomizer instancetype org.openxma.addons.ui.^table.
customizer.^xma.client.ITableCustomizer
The new lines are marked bold: First you define a unique name for the tableCustomizer.
Next, optionally you can specify an image that is used on the pagingControl for the button
used to invoke the tableCustomizer dialog. Of course the image has to exist in your projects
artifact in the specified path. And last you have to specify which TableCustomizer to use for
the pagingControl.
The implementation classes for the customizer interface are not included in the default project
classpath. So if you want tu use the tableCustomizer the projects classpath and your build
must be extended.
149
Paging and customizing Tables
Using the TableCustomizer means that queries used at the backend to display data can
change at runtime, depending from the filter settings applied in the TableCustomizer Dialog.
So these "filterable queries" have to be defined in the persistance model (dml), since queries
are not filterable by default.
You can define filterable queries like this:
repository CustomerDao for Customer {
table = "T_CUSTOMER"
operation Customer[] findCustomers() : from Customer as customer
}
service CustomerDas {
CustomerDao.crud
CustomerDao.findCustomers
filter=true
}
Caution: Do not use filter=true on operations that already have a order by-clause!
This can lead to erroneous HQL-queries. Initialize the sort order in the presentation layer, if
you want to predefine the sort order of the table data.
In the above example the findCustomers() Query defines an operation that is extended
with the filter keyword. So all information is available to link the filter query with the
TableCustomizer. In our Customer example the generated code on the server side looks like
this:
public QueryObject getCustomerTableQueryObject() {
int offset = customerTable_pagingControl.getOffset();
short pageSize = customerTable_pagingControl.getPageSize();
short sortingColumn = customerTable_pagingControl.getSortingColumn();
boolean isAscending = customerTable_pagingControl.getAscending();
XMASessionServer xmaSessionServer = (XMASessionServer)XMASessionServer.
getXMASession();
ITableCustomizerServer tableCustomizer =
(ITableCustomizerServer)xmaSessionServer.getComponent(Short.
valueOf(customerTable_customizerComponentId.getEncodedValue()));
QueryObject filterQueryForTable = null;
if (sortingColumn >-1) {
filterQueryForTable = tableCustomizer.
getQueryObject(sortingColumn,isAscending);
} else {
filterQueryForTable = tableCustomizer.getQueryObject();
}
filterQueryForTable.setFirstResult(offset);
filterQueryForTable.setMaxResults(pageSize);
return filterQueryForTable;
}
As you can see, the code uses the paging control information: offset, pageSize, and sorting
information is read to set up dynamically a generic filter query for the CustomerTable. The
returned QueryObject is actually the filter that has to be passed to the query as parameter.
The invokation of the query itself has to be done manually and the best place to do so is the
remote method that was defined in the model to react on the UI navigation events:
@Override
public void loadData(RemoteCall call, RemoteReply reply) {
Collection<CustomerView> findCustomers = null;
QueryObject queryObject = getCustomerTableQueryObject();
findCustomers = getTypedComponent().customerDas.findCustomers(queryObject);
getTypedComponent().setCustomers(findCustomers);
customerTableFill();
}
In the above sample implementation for the loadData remote call, the previously defined
filter query "findCustomers" is invoked and the filter that was retrieved from the generated
method getCustomerTableQueryObject() is passed as parameter.
150
Paging and customizing Tables
DDL for the TableCustomizer
Since the filter settings for the TableCustomizer can also be persisted in the database, a
datamodel definition is needed.
The DDL file for the TableCustomizer can be retrieved from the TableCustomizer project
itself for the Oracle DB. If used with other DB Vendors, the DDL has to be adapted according
to the vendor specific SQL language:
Link to Oracle DDL-File for TableCustomizer1
9.1.4 DDL for the TableCustomizer
Since the filter settings for the TableCustomizer can also be persisted in the database, a
datamodel definition is needed.
The DDL file for the TableCustomizer can be retrieved from the TableCustomizer project
itself for the Oracle DB. If used with other DB Vendors, the DDL has to be adapted according
to the vendor specific SQL language:
Link to Oracle DDL-File for TableCustomizer2
9.2 TableCustomizer Context Menu
Since version 5.0.4, the TableCustomizer has a new contextmenu feature, which allows quick
access to the customizer filter- and sorting features via the attached table's contextmenu.
Figure 9.3. Table context menu
In the above screenshot the context menu for the Depot column is displayed. By default, the
context menu offers the possibility to copy item values, quickfilter by those values, sorting
(in that case the column is already sorted, so the dialog offers to sort it the other direction or
to delete the sortorder. Additionally 2 custom entries have been added to the dialog.
Figure 9.4. Table context menu (generic)
The above screenshot shows a more generic context menu for columns, where no item was
clicked (ie. in the table header). Here the menu offers to open the filter dialog for that column
only whereby the user can bypass the broader tableCustomizer dialog. Since a filter was set,
the user also has the option to delete it.
1
2
http://svn.codehaus.org/openxma/org.openxma.addons/trunk/org.openxma.addons.ui.table/etc/database/create_tables.ddl
http://svn.codehaus.org/openxma/org.openxma.addons/trunk/org.openxma.addons.ui.table/etc/database/create_tables.ddl
151
Paging and customizing Tables
In the sorting area of the menu it allows the user to sort ascending and descending as well as
deleting the existing sort order. This means that in the background a sortorder was set, but
not with the highest priority (in this case the second - which is also why no sorting icon is
displayed for the column header). Changing the sortorder here (or by normally left clicking
the header column) sets the column to be the first priority sorting column.
9.2.1 Configuring the menu
In order to set up the default menu an object of ContextMenuHelper needs to simply be
instantiated on the table:
new ContextMenuHelper(myPage, myTable, myCustomizer);
If you want to extend the menu by your own custom entries or build it differently, you need
to extend the ContextMenuHelper class, ie. like this:
public class MyContextMenuHelper extends ContextMenuHelper {
MenuItem mnuDetails;
MenuItem mnuAccept;
public MyContextMenu(PageClient page, Table table, TableCustomizerComp
customizer) {
super(page, table, customizer);
}
@Override
protected void buildMenu() {
super.buildMenu();
if (getSelectedItem() != null) {
addMenuSeparator();
mnuDetails = addMenuItem(detailButtonW.getText(),detailButtonW.
getImage());
if (!isAccepted(getMenuItemKey())) {
mnuAccept = addMenuItem(acceptButtonW.getText(),acceptButtonW.
getImage());
}
}
}
@Override
protected void menuItemSelected(MenuItem menuItem) {
super.menuItemSelected(menuItem);
if (menuItem.equals(mnuDetails)) {
showDetails(getMenuItemKey());
} else if (menuItem.equals(mnuAccept)) {
executeAcceptFunction(getMenuItemKey());
}
}
}
9.3 Configuration Needed for the TableCustomizer
For runtime, the Table Customizer code has to be merged into the applications's war:
<!-- Ant Build File -->
<target name="preconditions">
<property name="table_ui_addon_war" value="${XMA}/org.openxma.addons.ui.table/
4_0_1/org.openxma.addons.ui.table_4.0.1.war"/>
<property name="table_ui_addon_jar" value="${XMA}/org.openxma.addons.ui.table/
4_0_1/org.openxma.addons.ui.table-facade_4.0.1.jar"/>
...
<target name="webapp" depends="createManifest, common_jars, component-jars">
152
Paging and customizing Tables
<copy file="${epclient_jar}" preservelastmodified="true" todir="${webappDir}/WEBINF/lib"/>
<!-- this is our working directory to assemble the xma webapplication -->
<mkdir dir="${webappDir}" />
<!-- For XMA Table Customizer -->
<unjar dest="${webappDir}" overwrite="true" src="${table_ui_addon_war}"/>
...
<path id="project.class.path">
<pathelement location="${table_ui_addon_jar}"/>
>pathelement location="${platform_client_jar}"/>
...
Also the xma-app.xml has to be adopted:
<component name="TableCustomizerComp" implPackage="org.openxma.addons.ui.table.
customizer.xma" >
<resource href="components/tablecustomizercompclient.jar" type="jar"
version="442fdcce3fcbe65fa3546fcfa1e8e25c" locale="de_AT" />
<resourceLink href="clientrt/xmartclient.jar" />
<resourceLink href="clientrt/epbase.jar" />
</component>
<resource href="components/addoncommonclient.jar" type="jar"
version="838b1ea95de0db7bfc2805314bc08fcb" />
In the applicationContext you have to add
<context:component-scan base-package="org.openxma.addons.ui.table"/>
..
<value>classpath:org/openxma/addons/ui/table/customizer/**/*.hbm.xml%lt;/value>
9.4 Export of Table Data into a .csv File
A common requirement for projects is the ability to export the data into a file. You can
configure the paging control to provide an additional button that handles the export (only
available for clients running on the Microsoft Windows platform).
Figure 9.5. Button to handle csv export
The configuration can be expressend in the style information for the paging control with the
export-keyword:
style pagingTable {
table-customizer : testTableCustomizer
paging-customizer-image : "/org/openxma/demo/customer/xma/client/cog_go.png"
paging-position : top
paging-style : custom
paging-jump-size : 10
paging-page-size : 10 max = 1000
paging-controls : back, customize, pagesize, export, next, fastnext, fastback,
info, reload, start, end
153
Paging and customizing Tables
}
If used with the tableCustomizer, the export keyword does more than just showing an
additional button. The code that is needed to handle the export is generated also. The exporter
uses additional classes that are not included in a standard openXMA Dsl project. See the
needed configuration details for the export utility. Notice that the generated code for handling
the export is only provided if the tableCustomizer is used. For the export functionality without
the tableCustomizer the export-implementation has to be provided manually.
On export, the CSV-File will be saved to the default directory for temporary files (using
the default encoding of the client VM) and opened with the default handler associated with
CSV-Files. Be aware, that encoding problems may occur in Microsoft Excel if the default
encoding is different to windows-1252!
In the serverside page there is an abstract method generated that must be implemented
manually. The implementor is asked here to provide the collection that is used as input
for the .csv table export. The name of the abstract method is built based on the table
name: queryFor<tableName>ExportData(QueryObject queryObject).
Take care to provide the right collection - it should correlate with the filter query used to
display the table data:
@Override
public Collection<?> queryForCustomerTableExportData(QueryObject queryObject) {
return getTypedComponent().customerDas.findCustomers(queryObject);
}
By default all table data is requested for export in one call to
queryFor<tableName>ExportData(QueryObject
queryObject). To
prevent huge amounts of data to be transferred in a single server request, the data set can be
split up into smaller chunks. The maximum number of rows to be transferred at once can be
specified in a client page override, e.g.:
@Override
protected int getCustomerTableExportBatchSize() {
return 1000;
}
The client will continuously read chunks with the given number of rows until all data
is transmitted. The generated default implementation gives 0, which means no split-up
transmission.
Observe: Be sure to comply with the given boundries in QueryObject at server side, or endless
loops may occur.
9.4.1 Configuration needed for the .csv export functionality
• First the classpath of the project must be extended with the openCSV lib - currently
version 2.0 is used.
• The lib must also be included for the build and provided at the client - example for
the ant build.xml file and also all the generated DTO (Data Transfer Object) - classes
that are used to fill the Table (simple POJOs) are needed on the client side :
<property name="openCSV" value="${INFRA}/opencsv/2_0/opencsv-2.0.jar"/>
...
<path id="project.class.path">
...
<pathelement location="${openCSV}"/>
</path>
154
Paging and customizing Tables
<target name="webapp" depends="common_jars,component-jars">
..
<copy file="${openCSV}" preservelastmodified="true" tofile="${webappDir}/
clientrt/openCSV.jar"/>
<xmachecksum file="${webappDir}/clientrt/openCSV.jar"/>
..
</target>
<target depends="compile" name="common_jars">
<fileset dir="${classDir}" id="commonclient_fileset">
...
..
.
<include name="org/openxma/demo/customer/**/dto/*.class"/>
</fileset>
</target>
• The library must be declared also as a resource in the app-descriptor file (xmaapp.xml)
<xma_application>
...
..
.
<resource href="clientrt/openCSV.jar" type="jar" version="" />
...
..
.
</xma_application>
155
Chapter 10: JSF Generator
This chapter describes the available possibilities used to use OpenXMA JSF generator.
10.1 Prerequisites
• XMA6 or higher
10.2 Creating a new XMAdsl JSF project with Wizard
1. Create a new DSL Project.
2. In the Project Layout, select Maven Standard Project as the template.
156
JSF Generator
3. On the last screen, select Java Server Faces as the client platform.
157
JSF Generator
4. Update project classpath
• Run the "2. update project setting", or
• Run "mvn eclipse:clean eclipse:eclipse -Declipse.useProjectReferences=false" from
the command line.
158
JSF Generator
5. Generate DEMO pml
• Open CustomerComponent.pml and run POM generator.
159
JSF Generator
6. Implement abstract methods
@Override
public void findCustomer() {
if (customer == null) {
customer = new CustomerView();
} else {
String firstName = customer.getFirstName() != null ? customer.
getFirstName() : "";
String lastName = customer.getLastName() != null ? customer.
getLastName() : "";
customers = component.customerDas.findAllLikeName("%" + firstName +
"%", "%" + lastName + "%");
customerTableTableModel.setWrappedData(customers);
}
}
7. Start application
• Run the task "Prepare FDM" in the ant file shown in Step 4 above, or run command
"mvn clean package -P development" from the command line.
• On command line, call "mvn jetty:run", or
• Start launch file src/test/resources/ Start "your project name" JSF FDM.launch
• Call "http://localhost:8080/your project name/pages/customerSearchForm.xhtml
160
JSF Generator
• Search for Customers!
10.3 JSF Error Message Presentation
In the OpenXMA JSF generertor, there are two ways to display validation messages.
• Using standard JSF error presentation
• Using Tooltips
By default, error messages are displayed using the standard JSF presentation style, however,
if you want to see the error messages in tooltips, you can configure the code generator
to generate the appropriate code. To configure the code generator to generate tooltips
for error messages, create a generator.properties file in src/main/resources and add a
property useTooltipForValidationMessage. Setting this property as true would generate
161
JSF Generator
error messages in tooltips, whereas, setting it as false would switch back to standard JSF
messages.
10.4 JSF Simple AJAX calls
In the OpenXMA JSF generertor, buttons are implemented with ajax calls to disable full page
refresh. For example, let's take the following pml code
command findCustomer
uicommand beforeFindCustomer
uicommand afterFindCustomer
eventmapping {
findButton
}
-> beforeFindCustomer, findCustomer, afterFindCustomer
In the above code, findCustomer, is a remote command and it will map to the actionListener
attribute of the command button. UI commands before the findCustomer will bind to onclick
event of the button and those after that will bind to the oncomplete event. An important thing
to keep in mind is that there must only be one remotecommand eventmapping for the button.
If there is more than one remotecommand, it will be ignored.
10.5 Internationlization
OpenXMA JSF generator supports automatication internationalization of labels and texts.
It creates a resourcebundle for each component and that resource bundle is loaded
into each page which is present in the component. For example, if your component is
present in org.demo.component namespace and it's name is ResourceComponent, then a
resource bundle is created in the src/generated/resources/org/demo/component folder with
name ResourceComponent.properties. The newly created resource bundle is loaded in the
component pages using the following code.
<f:loadBundle basename="org.demo.component.ResourceComponent" var="msgs"/>
In order to override any existing message inside the generated ResourceBundle or
add new messages, create a file in src/main/resources/org/demo/component with name
ResourceComponent_<<language code>>.properties.
10.6 AJAX Error Handling
OpenXMA JSF generator supports handling of error messages for ajax requests. This is
done through Primefaces Extensions ajax error handler. The required code for enabling ajax
handling is generated by default while creating a new project. To disable ajax error handling,
open src/main/webapp/templates/template.xhtml file and remove ajaxerrorhandler tags.
In case you want to enable ajax error handling in an existing project, please follow the steps
below.
1. Add Primefaces Extensions version 0.7.0 in the pom.xml
2. Create a file messages.properties in src/main/resources with the following content
ajaxerror.title=Error
ajaxerror.body=Server Exception: {error-message}
ajaxerror.body.viewexpired=View Expired. Please reload the screen.
162
JSF Generator
ajaxerror.button.reload=Reload
ajaxerror.button.hide=Hide
3. Add resource bundle to the faces-config.xml
<resource-bundle>
<base-name>messages</base-name>
<var>globalmsgs</var>
</resource-bundle>
4. Add primefaces extensions tag library declaration to the src\main\webapp\templates
\template.xhtml
5. Add the ajax error handler tags to the src\main\webapp\templates\template.xhtml. The
tags for ajax error handling are
<pe:ajaxErrorHandler
title="#{globalmsgs['ajaxerror.title']}"
body="#{globalmsgs['ajaxerror.body.viewexpired']}"
type="javax.faces.application.ViewExpiredException"
button="#{globalmsgs['ajaxerror.button.reload']}"
buttonOnclick="document.location.href=document.location.href;"/>
<pe:ajaxErrorHandler
title="#{globalmsgs['ajaxerror.title']}"
body="#{globalmsgs['ajaxerror.body']}"
button="#{globalmsgs['ajaxerror.button.hide']}"
widgetVar="myAjaxErrorHandler" buttonOnclick="myAjaxErrorHandler.
hide()"/>
10.7 JSF UI Customization
In the OpenXMA JSF generertor, two xhtml pages are generated for every page modeled in
PML. One page, ending with Gen.xhtml, contains the generated code, while the other page
is there for customizing the generated code.
However, there are alternative ways to customize the generated facelet code. These
approaches are described below:
1. Programmatic customization in backing bean
To customize the generated facelet code, override the customizeView() method in the
backing bean. A default empty implementation for the method is added to the generated
backing bean. It expects an argument, UIViewRoot, which could be used to access the
components of the page and modify them if needed. For example,
protected void customizeView(UIViewRoot viewRoot) {
UIComponent component = viewRoot.findComponent("CSearchPageForm:
firstName");
((HtmlInputText) component).setValue("UI Customization");
}
The above code finds the firstName field in the CSearchPageForm and then sets the initial
value of the field.
2. Customization through facelet code
Another option to customize the generated facelet code is through OpenXMA custom
styles. So, define a style and add a style property customization with value facelet like this
163
JSF Generator
style anyStyle {
customization: facelet
}
Apply this style on the items which needs to the customized. The way this will work is
that each element marked with the above style will get surrounded with <ui:insert> tags.
Now, once the elements are surrounded by <ui:insert> tags, we can override them using
<ui:define> tags in the custom pages.
10.8 JSF UI Automation Testing
Arquillian is recommended for writing automation test cases for OpenXMA JSF projects.
Arquillian provides following features.
• Container Lifecycle management: Arquillian manages starting and stopping of the
server, application deployments.
• Provides extensions: It has extensible architecture and provides various extensions
out of the box which makes testing of web application easier.
• Drone, an Arquillian extension, helps in managing the broswer lifecycle. It also injects
WebDriver instance in the test cases which makes testing applications easier.
• Graphene extension provided by Arquillian helps in selenium integration, stores
WebDriver instance in the Threadlocal so that we do not have to pass it in methods,
provides wait helpers which allows to wait for certain actions to complete.
To setup Arquillian in your project, please follow the steps below:
1. Set up Maven Dependencies
We will be running Arquillian in standalone mode. In standalone mode, the application
server needs to be started manually. For setting up Arquillian in standalone mode, we need
the following dependencies
<dependency>
<groupId>org.jboss.shrinkwrap.descriptors</groupId>
<artifactId>shrinkwrap-descriptors-spi</artifactId>
<version>2.0.0-alpha-5</version>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.protocol</groupId>
<artifactId>arquillian-protocol-servlet</artifactId>
<version>1.1.2.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-standalone</artifactId>
<version>1.1.2.Final</version>
<scope>test</scope>
</dependency>
<!-- Arquillian JUnit Standalone -->
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
164
JSF Generator
<artifactId>arquillian-graphene</artifactId>
<version>2.0.0.Final</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<!-- Graphene WebDriver -->
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
<artifactId>graphene-webdriver</artifactId>
<version>2.0.0.Final</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>jetty-io</artifactId>
<groupId>org.eclipse.jetty</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-bom</artifactId>
<type>pom</type>
<version>1.2.0.Final</version>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.2.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Please note that to run Arquillian test cases, we need JUnit 4.8.1 or higher.
2. Arquillian Configuration
Arquillian configuration is defined as an xml file. It must be available in classpath at the
time of executing test cases, which means you may copy it in src/test/resources folder
of your maven based project. In the standalone mode, the minimal configuration that is
needed is
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.
org/schema/arquillian/arquillian_1_0.xsd">
<extension qualifier="webdriver">
<property name="browserCapabilities">phantomjs</property>
<property name="phantomjs.binary.path"><!-- Path to Phantom JS exe
--></property>
<property name="dimensions">1280x1024</property>
</extension>
</arquillian>
In the above configuration, we have configured WebDriver to use GhostDriver(used by
PhantomJS). PhantomJS utilizes GhostDriver to carry out browser testing in headless
mode. For cofiguring Arquillian to use other browsers for testing, please refer https://
docs.jboss.org/author/display/ARQ/Drone.
3. Arquillian Tests
165
JSF Generator
Arquillian uses extensions to integrate with other frameworks like Selenium. For writing
automation test cases, we will use Graphene, Drone extensions provided by Arquillian
which helps in integrating with Selenium. In addition to integrating Selenium with
Arquillian, Drone helps in lifecycle management of browser. Graphene, on the other hand,
takes care of starting the selenium server, launching the web browser and do the clicking.
Once we have setup the dependencies and arquillian coniguration, we are ready for writing
test cases. Normally, for writing test cases, we need to write only the test cases. However,
for UI automation testing, we recommend using the Page Object pattern. On a high level,
Page Object pattern maps UI pages to classes and related actions to methods in the class.
All the actions specific to each page will be in a single class. So, for writing test cases, we
need to write test classes and Page Objects. We should append ITCase to all the integration
test classes. Let's take the example of writing the automation test case for find customer
use case in the JSF reference project.
Let's start by writing the PageObject for the search page.
import
import
import
import
import
org.jboss.arquillian.graphene.context.GrapheneContext;
org.jboss.arquillian.graphene.enricher.findby.ByJQuery;
org.openqa.selenium.By;
org.openqa.selenium.WebElement;
org.openqa.selenium.support.FindBy;
public class CSearchPage {
@FindBy(id = "CSearchPageForm:firstName1")
WebElement firstName;
@FindBy(id = "CSearchPageForm:lastName1")
WebElement lastName;
@FindBy(id = "CSearchPageForm:findButton")
WebElement findButton;
public static By PAGINATION_CURRENT = By.cssSelector("span.ui-paginatorcurrent");
public void findCustomer(String firstNameParam, String lastNameParam) {
firstName.clear();
firstName.sendKeys(firstNameParam);
lastName.clear();
lastName.sendKeys(lastNameParam);
findButton.click();
}
public void open() {
GrapheneContext.getProxy().get("http://localhost:8080/dsl-reference-jsf/pages/
JsfComponent/cSearchPage.xhtml");
}
}
Please note how WebElement are injected into the page objects. @FindBy annotation
helps in finding the web elements in the current page. It supports search by id, name, css,
class name, tag name, link text, partial link text and xpath. However, if you just need to
define the search expressions, you use By class, which supports all the above methods of
finding elements. However, By class does not support jQuery selectors. There is another
class ByJQuery which supports defining search expressions using jQuery selectors.
Now, it's time to write the test case.
import java.util.NoSuchElementException;
166
JSF Generator
import junit.framework.Assert;
import
import
import
import
import
import
import
import
org.jboss.arquillian.drone.api.annotation.Drone;
org.jboss.arquillian.graphene.spi.annotations.Page;
org.jboss.arquillian.junit.Arquillian;
org.junit.Test;
org.junit.runner.RunWith;
org.openqa.selenium.By;
org.openqa.selenium.WebDriver;
org.openqa.selenium.WebElement;
@RunWith(Arquillian.class)
public class CSearchPageITCase {
@Drone
WebDriver driver;
@Page
CSearchPage cSearchPage;
@Test
public void testSearch() {
cSearchPage.open();
cSearchPage.findCustomer("C", "C");
checkElementPresent(CSearchPage.PAGINATION_CURRENT, "Pagination current
element should be present");
WebElement webElement = getElement(CSearchPage.PAGINATION_CURRENT);
Assert.assertEquals("(1 of 3)", webElement.getText());
//test blank search
cSearchPage.findCustomer("", "");
checkElementPresent(CSearchPage.PAGINATION_CURRENT, "Pagination current
element should be present");
webElement = getElement(CSearchPage.PAGINATION_CURRENT);
Assert.assertEquals("(1 of 1)", webElement.getText());
}
// check is element is present on page, fail otherwise
private void checkElementPresent(By by, String errorMsg) {
try {
Assert.assertTrue(errorMsg, driver.findElement(by) != null);
} catch (NoSuchElementException e) {
Assert.fail(errorMsg);
}
}
// check is element is present on page, fail otherwise
private WebElement getElement(By by) {
try {
return driver.findElement(by);
} catch (NoSuchElementException e) {
return null;
}
}
}
Please note the naming convention of the test case. It ends with ITCase. This has been done
to exclude it from maven surefire plugin, which by default considers all classes ending
with Test as the test classes. For executing integration test cases, the recommended plugin
is maven failsafe plugin, which by default, considers classes ending with ITCase as test
classes. To run the integration test cases, after you have included failsafe plugin in your
maven confiuration, you should use the following command.
mvn verify -skipITs=false -P development
167
JSF Generator
For reference of the intrgration test cases, please download the
JSF reference project from http://svn.codehaus.org/openxma/org.openxma.dsl/trunk/
org.openxma.dsl.reference.jsf This project is a reference project for OpenXMA DSL JSF
applications. It also contains test cases for automation testing.
4. Arquillian Code Generated by OpenXMA
OpenXMA generates some code for Arquillian test cases. For every XMADslPage
(assume name of XMADslPage is CustomerSearchForm), it will generate the following
files.
a. CustomerSearchFormITCase: Arquillian test case, which acts as a Automation test
case.
b. CustomerSearchFormGen: Generated Arquillian Page Object, which contains the
WebElements based on the declaratation in DSL page.
c. CustomerSearchForm: Arquillian Page Object, which can be used for overriding the
default elements and programming the events on the web page.
5. Headless Testing
In browser testing is not friendly with continuous integration environments becuase all
continuous integration may not have a browser installed. PhantomJS, a headless webkit,
is used for headless testing of web applications. It is used in combination of GhostDriver,
which is a pure JavaScript implementation of the WebDriver wire protocol. It uses
PhantomJS as a backend.
In order to use PhantomJS and GhostDriver with Arquillian, please add the following
configuration of webdriver extension in arquillian.xml of your project.
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.
org/schema/arquillian/arquillian_1_0.xsd">
<extension qualifier="webdriver">
<property name="browserCapabilities">phantomjs</property>
<property name="phantomjs.binary.path"><<enter path here>></
property>
<property name="dimensions">1280x1024</property>
</extension>
</arquillian>
6. Locating Elements
You can locate elements on the browser using XPath and jQuery. There are some Chrome
browser plugins which can help in writing locators for elements.
• Xpath: Xpath helper
• jQuery: jQuery Debugger
10.9 JSF Invocation
XMA generates code for invoking one page/component from another component. JSF
generator also supports this feature. Please refer section Navigation commands for knowing
how to invoke pages/components from other components. JSF generator will generate hidden
fields for the values being passed and set their values before invoking the components. In
case you want to set the value of specific field manually, before invoking the component,
you could call the following javascript code.
168
JSF Generator
fieldWidgerVar.jq.val('valuetobeset');
Every XMA JSF project contains a scripts.js file, which has the client side APIs which can
be used for various common functionalities. You can have a look at the scripts.js for the
documentation of the APIs.
10.10 JSF Performance Testing
This section includes the steps to perform load testing on a JSF application.
1. Tools Required
• Java 1.5+
• Grinder: Grinder is a load testing framework that makes it easy to run a distributed
test using many load injector machines. Test scripts are written in Jython, and can
call out arbitrary Java code, providing support for testing a large range of network
protocols. It comes with a mature plug-in for testing HTTP services, HTTP scripts
can be recorded easily from a browser session. It can be downloaded from http://
sourceforge.net/projects/grinder/.
2. Installation
The installation of Grinder is pretty simple. After downloading it, just unzip it in a folder.
3. How Grinder Works
The Grinder framework is comprised of three types of processes: worker processes, agent
processess, and console. Worker processes interprets the test results and perform the tests.
Each worker process can run many tests in parallel using a number of worker threads.
Agent processes manages worker processes and also maintain a cache of test scripts. The
job of console is to coordinate with other processes, collate and display statistics. For more
information on how Grinder works, please visit http://grinder.sourceforge.net/g3/gettingstarted.html
4. Executing load tests using Grinder
The basic steps involved in running Grinder tests include:
• Writing (or generating) test scripts
• Writing a test properties file
• Executing test scripts
Let's discuss each of these steps in detail
• Writing (or generating) test scripts
Grinder test scripts can either be generated or coded manually. To code manually, one
needs to know Jython language and Grinder APIs. However, there is a shorter route.
One can generate the Grinder scripts using TCPProxy, a tool which can record the
HTTP traffic. You can follow the steps below to record the scripts using Grinder.
a. Set the proxy in your browser. By default, TCPProxy runs on port 8001.
b. Start the TCPProxy using the command
java -cp grinder.jar net.grinder.TCPProxy -console -http > test.py
The above command would capture all the http traffic from browser and create
grinder test scrips in file test.py
c. Record the scripts by browsing through the use cases which needs to be tested.
169
JSF Generator
d. When TCPProxy is started, an applet appears. To stop the recording process, just
click stop button on the applet.
• Writing a test properties file
We also need to create a properties file which contains properties to control the Grinder
testing process. Amongst others, this properties file can be used to control the number
of worker process, worker threads and number of runs of each worker thread. For
more information about this properties file, please refer http://grinder.sourceforge.net/
g3/properties.html. You need to set the following commonly used properties to your
grinder test properties file
#
# Commonly used properties
#
# The file name of the script to run.
#
# Relative paths are evaluated from the directory containing the
# properties file. The default is "grinder.py".
grinder.script = test.py
# The number of worker processes each agent should start. The default
# is 1.
grinder.processes = 2
# The number of worker threads each worker process should start. The
# default is 1.
grinder.threads = 2
# The number of runs each worker process will perform. When using the
# console this is usually set to 0, meaning "run until the console
# sneds a stop or reset signal". The default is 1.
grinder.runs = 5
# The IP address or host name that the agent and worker processes use
# to contact the console. The default is all the network interfaces
# of the local machine.
grinder.consoleHost = localhost
# The IP port that the agent and worker processes use to contact the
# console. Defaults to 6372.
grinder.consolePort = 6372
• Executing test scripts
Grinder test scripts can be executed in two ways
• Using Agent
To execute the scripts using Agent, just execute the following script from the
command line
java -cp grinder.jar net.grinder.Grinder test.properties
The above command will execute the test cases and store the result in the log
directory
• Using Console
Another way of executing tests is using the Console, which is GUI allowing
execution and analysis of test results from the user interface. To execute the test
cases using Console,
a. Start the Console using
java -cp grinder.jar net.grinder.Console
170
JSF Generator
b. Start the Agent
java -cp grinder.jar net.grinder.Grinder test.properties
Please note that the order of steps while running the steps using console. Console
should be started first followed by the Agent. Once both Console and Agent are
started, Agent will wait for further signals from the Console to start the tests.
10.11 JSF Performance Best Practices
In this section there are some tips which could be followed to improve performance of
JSF applications. These tips are in addition to what we normally follow in our application
development during development and deployment, which are not covered here.
• Enable Server side state saving as it performs better. To enable it, set the values of
javax.faces.STATE_SAVING_METHOD context-param to server.
• Disable compression of state in server. To disable state compression, set
org.apache.myfaces.COMPRESS_STATE_IN_SESSION as false
• Disable serialization of state in session in case of non-clustered
environments. To disable serialization of state in session, set
org.apache.myfaces.SERIALIZE_STATE_IN_SESSION as false
• By default, Myfaces renders the HTML in "human readable" format, to improve
performance, we can disable that by setting org.apache.myfaces.PRETTY_HTML as
false
• Based on the your application's requirements, you can reduce the number of
views in session, which defaults to 20. To override the default value, set
org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION to the desired value
• Disable development mode for facelets by setting facelet.DEVELOPMENT as false
• To remove comments from response/request, set facelets.SKIP_COMMENTS to true
• Turn off facelets refresh trigger in prouction environments by setting
facelets.REFRESH_PERIOD to -1
10.12 JSF IDE
This section contains the information about JSF IDE that you can use for better code
completion features and productivity.
Among the available JSF IDEs, JBoss Tools is the recommended IDE for JSF development,
which is available as an Eclipse plugin. The home page of JSF IDE is https://www.jboss.org/
tools/download/dev/4_1. For a detailed list of features, please refer http://docs.jboss.org/
tools/4.1.0.Final/en/jsf_tools_ref_guide/html_single/index.html
One of the important features for Java Server Faces in JBoss Tools is code completion of
managed(or backing) bean methods and properties inside facelets. However, this feature
will not work for OpenXMA generated Managed beans and facelets. The reason is that
the OpenXMA generated managed Beans are instantiated and managed as Spring beans
and JBoss tools does not support Spring beans for code completion inside facelets. It only
supports CDI beans, beans having @ManagedBean annotation and beans declared in facesconfig.xml for code assist inside facelets. So, if you need code completion functionality
inside facelets for backing beans, you may choose to add @ManagedBean annotation on
top of your backing bean. Adding @ManagedBean will allow code completion for backing
bean methods and properties in the facelets. We have tested that adding @ManagedBean to
the backing beans will not affect the functionality, but still, it is advisable to to remove the
annotation before deploying the code in production.
171
JSF Generator
Removing @ManagedBean annotations from the backing beans may be cumbersome. So,
we can remove them during the build time using a maven plugin. One such maven plugin is
maven-replacer-plugin (https://code.google.com/p/maven-replacer-plugin/), which replaces
tokens within a file with a given values and fully supports regular expressions. Configuration
of maven-replacer-plugin to replace @ManagedBean from java files is
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.2</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>replace</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>/src/main/java/**/*.java</include>
</includes>
<replacements>
<replacement>
<token>@ManagedBean</token>
<value></value>
</replacement>
<replacement>
<token>import javax.faces.bean.ManagedBean;</token>
<value></value>
</replacement>
</replacements>
</configuration>
</plugin>
10.13 JSF Logging
In OpenXMA JSF applications, logging is done using slf4j and logback libraries. When u
create a new project using Project Creation Wizard, dependencies for slf4j and logback are
automatically included in the pom.xml file. In addition, a logback.xml containing a basic
configuration is also created. This logback configuration outputs the log messages on the
console.
Note: Please run mvn eclipse:clean eclipse:eclipse once before launching the
server to resolve the classpath dependencies.
10.14 XSS Protection
Cross-Site scripting(XSS) is a common security attack that happens on the web. One of the
ways to prevent XSS is to sanitize the HTML input. In XMA, this is done by providing
XSSProtectionFilter which uses AntiSamy library to clean the HTML input. To configure
XSSProtectionFilter, please add the following configuration in web.xml.
<filter>
<filter-name>xssProtectionFilter</filter-name>
<filter-class>org.openxma.dsl.platform.security.XSSProtectionFilter</filterclass>
</filter>
<filter-mapping>
<filter-name>xssProtectionFilter</filter-name>
172
JSF Generator
<url-pattern>/*</url-pattern>
</filter-mapping>
Antisamy library needs a set of rules, known as policy, which are used to sanitize the xml. For
XSSProtectionFilter as well we need a policy file. To supply an antisamy policy file, you can
configure a filter parameter, antisamy-policy-file-name, and it's value should be the name of
the policy file. The policy file should be kept in root of the classpath, for example, in src/
main/resources folder of a typical maven project. However, the parameter is not mandatory,
and in the abscence of the parameter, a default policy file will be used which will remove
script tags from the HTML.
10.15 URL Rewriting
OpenXMA JSF projects support URL rewriting out of the box. Whenever a new OpenXMA
DSL project is created for JSF, URL rewriting components are added to the project
automatically. OpenXMA uses URL rewriting functionality provided by http://tuckey.org/
urlrewrite/. To enable url rewriting in an existing project,
1. Add the dependency of URLRewriteFilter library in the pom
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.3</version>
</dependency>
2. Declare the URL rewrite filter in web.xml. Please make sure that the filter mapping for
this filter is before any other filter or servlet mapping. The only exception is that it can
come after CsrfPreventionFilter, see section 1.16 below.
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
3. Create urlrewrite.xml file in the WEB-INF folder and add rewrite rules.
4. For more information, please refer http://tuckey.org/urlrewrite/
10.16 CSRF Protection
Cross Site Request Forgery (CSRF) is one of the common attacks that happen to web based
applications. One of the recommended ways to prevent CSRF attack is to use Synchronizer
Token Pattern. The synchronizer token pattern requires the generating of random "challenge"
tokens that are associated with the user's current session. These challenge tokens are then
inserted within the HTML forms and links associated with sensitive server-side operations.
When the user wishes to invoke these sensitive operations, the HTTP request should include
this challenge token. It is then the responsibility of the server application to verify the
existence and correctness of this token. By including a challenge token with each request,
the developer has a strong control to verify that the user actually intended to submit the
desired requests. Inclusion of a required security token in HTTP requests associated with
173
JSF Generator
sensitive business functions helps mitigate CSRF attacks as successful exploitation assumes
the attacker knows the randomly generated token for the target victim's session.
OpenXMA based JSF application provide CSRF protection out of the box. It is implemented
by a server side filter org.openxma.dsl.platform.security.CsrfPreventionFilter. In new
OpenXMA JSF project, CSRF protection is enabled by default. To configure it in an existing
OpenXMA application, please follow the information below.
1. Declare the CsrfPreventionFilter in web.xml. Please make sure that the filter mapping for
this filter is before any other filter or servlet mapping
<filter>
<filter-name>CsrfPreventionFilter</filter-name>
<filter-class>org.openxma.dsl.platform.security.CsrfPreventionFilter</filterclass>
<init-param>
<param-name>unProtectedUrls</param-name>
<param-value>/index,*.js.xhtml,*.css.xhtml,*.gif.xhtml,*.gif,*.png.xhtml,*.
png</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CsrfPreventionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
In the configuration above, we have configured unProtectedUrls initialization parameter
which declares URLs or URL patterns which would not be checked for CSRF
synchronizer token. Using this parameter, you can configure the landing page of the
application, which is /index in the case. The landing page is not checked for CSRF
synchronizer token.
2. Add additional initialization paramters, if needed. All the supported initialization
paramters are documented below.
S. No
Parameter Name
Description
Default
1
unProtectedUrls
URLs or URL patterns which will not
be checked for CSRF synchronizer
token
None
2
randomClass
The name of the class to use to
generate nonces. The class must be
an instance of java.util.Random
java.security.SecureRandom
3
nonceParameterName
Name of Synchronizer token
parameter
csrf_nonce
4
nonceSessionAttributeName Name of session attribute in which
previously generated nonces are
stored
5
nonceCacheSize
The number of previously issued
20
nonces that will be cached on a LRU
basis to support parallel requests,
limited use of the refresh and back
in the browser and similar behaviors
that may result in the submission of a
previous nonce rather than the current
one.
6
denyStatus
HTTP response status code that is
used when rejecting denied request.
Table 10.1.
174
csrf_nonce
403
JSF Generator
10.17 Validations
10.17.1 Server Side Validations
Validations in OpenXMA JSF can be done either using standard JSF validations or using JSR
303 validations. It is recommended to use JSR 303 bean validations. In an existing project,
JSR 303 validations are activated using the project prefereces in Eclipse and in a new project
using the Project Creation Wizard in a new project.
Please note that if OpenXMA code generator is configured in the pom file, we need
to pass JSR 303 configuration to the OpenXMA code generation maven plugin via
generator.properties file. To do that, please add the property domainModel.jsr303validation
to the generator.properties file and set it value as true.
Once the option is enabled, the generated domain objects will have JSR 303 validations.
10.17.2 Error Message Resolution
OpenXMA provides enhanced error message resolution. Please see section 5.9.2 for the
details of error message resolution. To enable OpenXMA Jsr 303 validator, please add the
following configuraton to the Spring application context.
175
JSF Generator
<bean id="validatorResourceBundle" class="org.openxma.dsl.platform.
validation.Jsr303ValidationMessageSource">
<property name="basenames">
<list><value>ValidationMessages</value></list>
</property>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.
LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="validatorResourceBundle" />
</bean>
<bean class="org.springframework.web.context.support.
ServletContextAttributeExporter">
<property name="attributes">
<map><entry key="javax.faces.validator.beanValidator.
ValidatorFactory"<ref bean="validator" /></entry></map>
</property>
</bean>
Add a resource bundle ValidationMessages.properties in the src/main/resources folder.
10.17.3 Partial Validations
In a scenario where you want to validate only few fields of a form, then you can pass those
fields using process attribute of the command button. To do that override the command button
in the manual facelet and add the process attribute with the ids of the fields which needs to
be validated. For overriding the command button, please section 10.7
10.18 Visible Flag
OpenXMA JSF supports visible flag which controls the visibility of the fields. To ensure
that it works properly, you need to add a style to your project's stylesheet. Please add the
following style if it is not already present.
.hide {
display:none;
}
10.19 Full Page Navigation
To facilitate full page navigation while calling one page from another, OpenXMA Pom DSL
has been enhanced and the functionality can be achieved using the following syntax.
command editSelectedCustomer : invoke CustEditForm in window customerTable.
getSelectedValue() -> customerOid
In the above scenario, when editSelectedCustomer command is invoked, Browser will
be redirected(POST-Redirect-GET in practice) to the CustEditForm and CustomerTable's
selected value is populated in the customerOid.
10.20 Remote Debugging
OpenXMA JSF provides launch script for launching remote debugging in src/test/resources
folder of every project. The script will have name "Project Name"-remote-debug.launch.
176
JSF Generator
Before launching this script, please make sure that server has been launched in debug mode.
Please check individual server documentation to launch a server in debug mode.
If you are using Maven Jetty Plugin to test the application, then Jetty can be launched in
debug mode by setting the following environment variable
set MAVEN_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:
transport=dt_socket,address=8000,server=y,suspend=n
10.21 JSF datatable
Primefaces Datatable rovides pagination capabilities. OpenXMA JSF also provides support
for properties which can be translated into Primefaces Datatable pagination properties. This
can be done using a table customizer style. Please see Paging Chapter for more details.
The following properties are supported in PML for Primefaces datatable
1. paging-page-size
This property controls the number of records to be displayed in datatable in one page.
Primefaces datatable also supports a selection box from which one can configure the
number of records that can be displayed in one page. For example, in the following PML
code
paging-page-size: 10 max=50
Page size is by default set to 10. The selection box will have the option to select page size
of upto 50 with an increment of 10. The selection box will have entries 10,20,30,40 and 50
2. paging-position
This property controls the position of the paging controls. The possible values are top and
bottom. Omitting this value will place the paging control on both, top and bottom of the
page.
3. paging-controls
Primefaces datatable provides pagination template to control the display
of pagination controls. It supports the following controls FirstPageLink,
LastPageLink, PreviousPageLink, NextPageLink, PageLinks, CurrentPageReport,
RowsPerPageDropdown. Below are the mapping of these with PML attributes of pagingcontrols style
• FirstPageLink (PML: start)
• LastPageLink (PML: end)
• PreviousPageLink (PML: back)
• NextPageLink (PML: next)
• PageLinks (shown by default as PML counterpart is not there)
• CurrentPageReport (PML: info)
• RowsPerPageDropdown (PML: pagesize)
The controls will be displayed in this order CurrentPageReport, FirstPageLink,
PreviousPageLink, PageLinks, NextPageLink, LastPageLink, RowsPerPageDropdown.
If paging-control property does not exists in the table customizer, then all the above
controls will be visible.
177
JSF Generator
10.22 BootStrap Navbar Component
Bootstrap navbar is exposed as a JSF component in OpenXMA JSF. You can build navbar
using the following tags.
1. navbar: Use this tag to create a Bootstrap navbar. Available attributes are:
• brand: Bootstrap brand.
2. menu: Use this tag to create a dropdown menu
• name: Name of the menu that will appear in navigation bar
• value: Link to which the menu point. Value is optional and can be used when menu
is just a link and not a dropdown menu.
3. separator: Use this tag to introduce a separator between two menu items.
Following program listing shows a Bootstrap navbar built using OpenXMA JSF components.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:o="http://myfaces.apache.org/orchestra"
xmlns:xma="http://openxma.com/xmajsf"
xmlns:h="http://java.sun.com/jsf/html">
<o:separateConversationContext>
<xma:navbar brand="#{globalmsgs['template.project.xmaReferenceJsf']}">
<xma:menu name="#{globalmsgs['template.project.customersMenu']}">
<h:outputLink value="#{request.contextPath}/jsf/cSearchPage">#
{globalmsgs['template.project.customers']}</h:outputLink>
<h:outputLink value="#{request.contextPath}/customer/
customerSearchForm">#{globalmsgs['template.project.customercomponent']}</h:
outputLink>
<h:outputLink value="#{request.contextPath}/jsf/validationPage">#
{globalmsgs['template.project.validations']}</h:outputLink>
<h:outputLink value="#{request.contextPath}/jsf/custSearchForm">#
{globalmsgs['template.project.fullPageNavigation']}</h:outputLink>
</xma:menu>
<xma:menu name="#{globalmsgs['template.project.basic']}">
<h:outputLink value="#{request.contextPath}/jsf/parentWindow">#
{globalmsgs['template.project.parentchild']}</h:outputLink>
<h:outputLink value="#{request.contextPath}/jsf/invokeComponentPage">#
{globalmsgs['template.project.invokeComponent']}</h:outputLink>
<h:outputLink value="#{request.contextPath}/jsf/remoteCallsPage">#
{globalmsgs['template.project.remotecall']}</h:outputLink>
<xma:separator/>
<h:outputLink value="#{request.contextPath}/jsf/chartsPage">#
{globalmsgs['template.project.charts']}</h:outputLink>
<h:outputLink value="#{request.contextPath}/jsf/googleMapsPage">#
{globalmsgs['template.project.maps']}</h:outputLink>
<h:outputLink value="#{request.contextPath}/jsf/widgetsPage">#
{globalmsgs['template.project.widgets']}</h:outputLink>
</xma:menu>
</xma:navbar>
</o:separateConversationContext>
</html>
10.23 Toggle Button
OpenXMA JSF supports Primefaces toggle button, http://www.primefaces.org/showcase/ui/
selectBooleanButton.jsf. A button can be converted into a toggle button using two ways
1. Using the toggle atrribute: Set the toggle attribute of the button as true
178
JSF Generator
2. Using Styles: Create a style and set the toggle attribute of the style as true and apply this
style on the button.
179
Glossary
DAO
Data Acess Objects (DAO) are a design pattern to seperate database access code from other code
parts in an uniform manner.
IOC
In the Java community there's been a rush of lightweight containers that help to assemble
components from different projects into a cohesive application. Underlying these containers is a
common pattern to how they perform the wiring, a concept they refer under the very generic name
of "Inversion of Control".
See http://martinfowler.com/articles/injection.html
MDD
Model-Driven Design
http://domaindrivendesign.org/discussion/blog/evans_eric_ddd_and_mdd.html
MDSD
Model-Driven Software Development (MDSD) refers to the systematic use of models as primary
engineering artifacts throughout the engineering lifecycle. MDSD can be applied to software,
system, and data engineering.
RBACL
In computer systems security, role-based access control (RBAC) is an approach to restricting system
access to authorized users. It is a newer alternative approach to mandatory access control (MAC)
and discretionary access control (DAC). RBAC is sometimes referred to as role based security.
See http://en.wikipedia.org/wiki/Role-based_access_control
180
Bibliography
[1] Addison Wesley. August 20, 2003. Domain-Driven Design: Tackling Complexity in the Heart of
Software. 0-321-12521-5. http://domaindrivendesign.org/books/ .
[2] Pearson Education. January 2003. Patterns of Enterprise Application Architecture. 0321127420.
http://martinfowler.com/books.html .
181