- 1 Introduction
- 2 Annotated Models, Annotation generation with manual overrides
- 3 Runtime Model
- 4 Working with generated code
This page gives an overview of the main design ideas which are the basis for the Texo project.
Annotated Models, Annotation generation with manual overrides
Generating and working with model annotations is the core concept of Texo development.
A model annotation is very similar to a Java source code annotation. So just as you can annotate a java class or field it is possible to annotate a model also (its EClass and EStructuralFeature). A very good example of a project making use of annotated models is the Teneo project which makes use of JPA annotations in the model to support of persistence of EMF objects in relational databases.
Annotated models corresponds to what in other concepts or generation frameworks is called stereotypes or enriched models. It is a common concept in code generation, i.e. software artifact generation often consists of two steps:
- read the model and enrich it with additional information
- call a template to generate the artifact using the enriched model
It is important to enrich the model in step 1 because it is far easier to implement a template if all the main generation choices have been made up-front. The template then just has to follow the decisions reflected in the enriched model.
Texo uses the same 'enriched' model approach and adds other important concepts:
- makes model-enrichment more explicit by explicitly modeling the enrichment (== annotation models): an enriched model corresponds in Texo terminology to an annotated model.
- explicitly allows developers to before-hand (partially) annotate models, Texo maintains these manual annotations and adds missing annotations to make the model fully annotated before actually generating the software artifact.
- uses model annotations explicitly also as part of the output, so the model annotations are used to generate annotations in the generated Java code, or is used to generate a separate file (orm.xml).
A future extension is to also make sure that model annotations are available at runtime so that they can drive runtime behavior.
Texo makes model enrichment explicit. Texo works with annotation models, these models describe the annotations which are allowed and supported. Texo providing an extendable framework supporting new annotation models and annotators. Initially Texo will support two annotation models (annotation sets):
- annotations for model generation itself (to set the name of java class for example)
- JPA annotations: to support generation of JPA annotations in the source code or in a separate orm.xml file
In a next phase new annotation models are planned to be added, for example for EJB3 (Seam) or Hibernate Search.
An important role is implemented by Model Annotators. A model annotator is responsible for generating model annotations for a specific domain (for example for JPA, JAXB, Hibernate Search, etc.). A model annotator gets as an input a model which can be partially annotated. Based on this input it creates/adds annotations the model. The annotator outputs a fully completely annotated model. In one artifact generation process multiple annotators can/will work to create a fully annotated model.
A model annotator must adhere to the following rules:
- It should be able to handle not-annotated models. So even if a model is passed in without annotations then an annotator is responsible for outputting a fully and correctly annotated model.
- It should not overwrite annotations which exist in the original model (these have been added by a developer for example). The annotator may only add annotations or set information in an annotation which has not been set yet.
- It must always fully annotate a model, the output is always a complete, correct and fully annotated model. So next actors (template) in the generation process can always assume that an annotation is present and complete.
The basic flow of a Texo artifact generation process using annotators then consists of the following steps:
- Developer annotates the model where he/she wants to override the default annotation behavior
- Texo reads the model and its manual annotations
- Different annotators are called to make sure that the model is fully annotated. Some important aspects:
- an annotator may not overwrite a manual annotation
- there can be multiple annotators which are executed after eachother, this to support different annotation models.
- the fully annotated model is passed to the template for generating the sofware artifacts
Benefits of Annotated Models, Model Annotation Sets and Model Annotators
Texo provides a framework which makes it possible to support multiple annotation models for one model and in one common generation process. Texo makes this possible because it explicitly defines and models Annotations and implements an API for Model Annotators.
With Texo developers have a means to control the generation process and output by setting annotations beforehand. The annotations generated by annotators and used by templates are the same annotations which can be manually set by a user. This gives the user the same level of control as the generation logic itself.
Annotators can work with non-annotated models. This makes it easy to start using Texo and annotated models. Also as Annotators can work with partially annotated models, a user only needs to override the parts which need a different approach.
Texo makes it possible to add more annotation models and annotators over time.
Texo differs from other code generation approaches (except for EMF itself) in that specific attention is paid to support of a runtime representation of the domain model.
The domain model is expressed in ecore or XSD and with the model annotations drives the code generation. At runtime it is important to also have the possibility to work with this domain model and to access the generated code using model constructs.
Model-driven functionality (at runtime)
Many applications have functions which make sense to implement at model level instead of at specific instance/type level. Some examples:
- archiving: implement a generic archiving model which uses the runtime model to interrogate objects and retrieve changed and new property values.
- security: define security using entities from the domain model. To support security definition changes at runtime it must be possible to address the model at runtime. To check security at runtime one needs access to the runtime model also (for specific instances).
- export/import: export and import functions in an application can benefit from generic model-based logic. Implementing export/import using the runtime model is very efficient compared to writing a specific export/import per type in a system.
Another usage scenario for having the model available at runtime is that the model elements can be used to define other system components. An example of this is a system which allows users/consultants to define the user interface themselves through a user interface. To support the user/consultant in defining a user interface he/she must be able to select elements from the runtime model to define fields in a form or columns in a grid.
Runtime Model in Texo
The Texo runtime model functionality makes it possible to:
- access the available EPackages which have been loaded/initialized and iterate over its EClassifiers and EFeatures.
- (at runtime) determine the domain type (the EClass) represented by a pojo.
- access (get/set) the values of EFeatures in a pojo at runtime
Texo takes a different approach than EMF in making the runtime model available. In EMF each object always implements the EMF EObject interface or extends the EMF BasicEObjectImpl. Texo implements the runtime model outside of the pojo in a separate generated class which acts as a wrapper of the pojo to give it a 'Model-Face'.
The Texo runtime model is an ecore model (EPackage, EClass, etc.). The runtime model is accessible using the following classes:
- The ecore model can be reached through the generated ModelPackage class (see image on the right). The ModelPackage provides access to the EClassifiers (EClass, EEnum) and EStructuralFeatures of a model.
- The ModelFactory provides access to String converters and the generated Model wrappers.
- The ModelResolver class (provided by the org.eclipse.emf.texo plugin) acts as a Model registry and manages all initialized and loaded ModelPackages.
For more information see this separate page on the Runtime Model.
The current implementation of the Runtime Model access logic is based on several trials and study of different approaches. For a short description on these other approaches see this page.
Working with generated code
There are roughly two schools of thought regarding how to handle and work with generated code.
One school of thought states that generated code should never be changed manually. Generated code should not be committed to a CVS. It should be possible to remove all the generated code, regenerate and then have the same situation as before. In this approach any manual/custom logic should be implemented in subclasses of the generated code. These subclasses can also be generated. This school of thought is what is propagated by developers from the XPand project.
The other school of thought states that it should be possible to change generated code. manual changes in the code should remain even if the code is re-generated. Generated code should be checked in to a CVS. This approach is followed by the EMF project.
Both groups consist of very experienced and very knowledgeable experts on the field of model-to-text transformation and code generation.
Texo follows the EMF approach (the second school of thought) because:
- it feels more natural to change/add behavior to the generated class than to a 'dummy' subclass.
- less classes are generated and the generated classes are kept in sync with the model.
- manual code can easily be identified inside of generated classes.
- the code generation does not update the file on the file system if it has not changed by code generation. This results in minimal changesets in a CVS, and much better, if something changes it is directly visible in CVS diffs etc.
- by checking in the generated code to cvs it is much easier for others to work with the development project as no generation step is needed after getting the project or updates from a CVS. The same applies to re-distribution of regenerated code, by checking in the generated code (and the changes) it is much clearer what has changed and when people update from the CVS they will receive a consistent state directly.
- Texo will re-organize imports and remove files which related to model elements which have been removed. As the code is checked in these removal actions are tracked in a CVS.