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
© Copyright 2024