Code generator
Contents |
Code generation presentation
Ofbiz-Generator is a technical component with the goal to help functional developer (or business analyst) to develop more quicker extention to existing OFBiz component or new OFBiz component. This component automaticaly generate from UML Class diagram most of the ofbiz files need to a ofbiz component.
- entitydef.xml : entity definition for the entity engine.
- servicedef.xml : base service definition.
- {entity}ClassObject.java : one file per class to be able to have one object by entiy.
- {entity}Service.java : one file per class to be able to manage user entry check for standard Forms (depend of modelization).
- {Component}Data.xml : one file per component with status or enum value
- UiLable{Component}.properties : one file per component with all uilabel use in the other generate components
- form{Entity}.xml : one file per class for standard Forms (depend of modelization), with
- edit{Entity}
- list{Entity}
- editAssoc{Entiy}
- show{Entity}
- {Component}Screen.xml : widget filen, one file per component, use to easily creat sub-component file to manage screen
- edit{Entity}.bsh :
- find{Entity}.bsh :
- editAssoc{Entity}.bsh :
- controler.xml :
With file generation we have OFBiz best practice uses for most of the file (all the generated and so most of the developed). We have better object approach because one object is available for each entity. All file developed are realized as extend of the generated file so it's possible to regenerate the file when a new technology or best practice appair.
This component contain all the generator sources and the process to build the generator.jar use in the other components.
You can generate file in each component by running ant in the modeldef directory, all the generated file are in the modeldef/target/gen directory.
In each functional componant, there is a modeldef/src/zuml directory with the UML Class diagram. You can use most of the UML rules, some complementary information must be put in specific tag, read the neogia/website/doc/UmlModelizationRules_Tags.dbk to have the tag list and how to use it.
UML modelization rules
Go to the UML modelization rules page to have detail on UML modelisation rule we are using to be able to generated ofbiz code
Developping a neogia module from scratch
There are situation when none of the available functionalities in Ofbiz Neogia would fullfil your needs. In those situation you find yourself forced to develop your own module. don't feel sad, because you'll surely take profit of the Party Module which would fournish you with a good user managment support. That's not all the background Ofbiz framework with its entity engine, service engine, widgets along with neogia generators would make an excellent starting point, which would put you in most cases at the end stage of the development process, where other development techniques would require more efforts.
there is so much talk about neogia generators but no consistent and current documentation exists. When I began to use this neogia feature I used the neogia for dummies book. The book talks about generation with maven 1.0 project manager tool. The fact is that the process of generation has evolved in a quick pace and the documentation available no longer holds true (except from some basic principles in creating the uml domain class model ).
Scope
In this document the full process from uml design to a fully functioning ofbiz-neogia component is shown. This document will not explain ofbiz internals and what is the aim of each generated piece. Actually you can begin with the one of Basil Argasosy (OFBiz An Insider View). the hello world examples from Si Chen found on the openstrategies website are also helpful.
The component will be a parking management application. The application is actually straight forward, it fullfils the need for a company that holds many parking spaces and numerous cars. The company wants to know at any time which car is parking in which parking and within which parking place. More features will be considered in the future to see some in depth ofbiz or neogia features, but for the time being let's keep it simple and stupid.
Parking application
When considering neogia components someone will notice that they are different from other ofbiz components in these respects :
- they are located in the /neogia subfolder.
- they have a folder called modeldef that lies beside other ofbiz specific folders (entitydef, config, servicedef, webapp, widgets, ...)
The latter point is the most important. Actually with neogia the modeldef is the offspring that gives birth to other folders and their content. We will see how later down the document.
The structure of the newly created neogia component is the following
/ofbizNeogia
/neogia
/parking
/modeldef
/zuml
parking.zuml
build.xml
project.properties
/config
The zuml file
Neogia generators generate ofbiz application pieces from an uml model based on the xmi XML schema. It seems that the preferred UML tool for neogia community is Poseidon Community Edition version.
The right thing to do is to begin with an existing neogia component uml model and remove unecessary classes and packages from the model, You should keep in mind that your packages names must begin with "org.neogia" and finish with "entities". and when entities are generated they would have their package names begin with "org.ofbiz". That's what Neogia people decided. Anyway it shouldn't harm anyone.
Here is the resulted model :
It is important to know that all classes present in the model belong to the 'org.neogia.parking.manage.entities' package. When the generation is complete all classes will belong to the 'org.ofbiz.parking.manage' package.
Even though tagged values are explained elsewhere, I will present the tagged values used in the above model.
- primaryKey: this tag define an attribute as the entity's primary key when set to 'true'. I used this tag on the 'sequenceId' attribute present for each entity.
- value: this tag gives a default value for the attribute. Actually I set this value for the sequenceId also. I set it to 'nextSeqId' which is a keyword that tells the generators that this attribute value will take the next value on a sequence.
- gui: this tag gives directives on how the UI will be generated. I used this values:
- indexed: this value is used with an attribute to make it indexed and also available on the search form correspondig to its entity
- calculated: this value is used to specify that the attribute value is actually calculated instead of user specified. I used this tag:value occurence with sequenceId attribute.
- list,edit,show,lookup: these gui tag values tell neogia generators which screens and services would be generated for the corresponding entity (these are entity specific attributes, they can't be used with attributes).
- editAssoc: this is a great feature for me, it generates a screen that helps create associated entities to a given entity. It must be defined at the end of the relationship cocnerning the related entity. for instance I used this tagged value on the ends of the realtionships concerning the ParkCar entity to CarOperation, Insurance, ....
Other conventions must be followed: all attributes and association ends are with protected modifier. I invite anybody interested by this example zuml file to drop an email to the ML.
The generation process
Actually the generation is made with the 'neogia:generate' ant target from within the modeldef folder. The build.xml can be taken from any of the other neogia components, but if you want to automate the installation process (i.e: copy the generated files to their proper place for normal component build and execution), the script must be customized. I didn't change it myself as I took the pieces generated in 'modeldef/target/gen' one by one to glue them together. Once I find myself confident with the result, I will proceed in making the build.xml neogia:install target complete and put it available in here.
Before launching the generation some porperties must be changed in 'project.properties' and 'build.xml' files. In this case, since I took a copy of the files concerning the quality neogia component, I just changed every occurence of quality to parking, I respected the letter cases (QUALITY to PARKING, Quality to Parking, quality to parking)
As I told earlier the generated files are put by ant in modeldef/target/gen/.
Use generated pieces to build the application
Some generated pieces can be used as is, but some others must be modified before they can be useful. In this section I will talk about how to build the parking component from the generated code.
entitydef files
I copied this folder with its content without modification to the "parking" component root folder /neogia/parking
Here is the listing of the two generated files "entitygroup.xml" which defines the entities used by the component, and "entitymodel.xml" which defines the attributes of each of the entities and relationships between them along with the cardinality. Headers of the files have been removed.
<entitygroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/entitygroup.xsd"> <entity-group group="org.ofbiz" entity="DriveRecord"/> <entity-group group="org.ofbiz" entity="OperationType"/> <entity-group group="org.ofbiz" entity="ParkCar"/> <entity-group group="org.ofbiz" entity="ParkCarEnergy"/> <entity-group group="org.ofbiz" entity="ParkCarInsurance"/> <entity-group group="org.ofbiz" entity="ParkCarKind"/> <entity-group group="org.ofbiz" entity="ParkCarMark"/> <entity-group group="org.ofbiz" entity="ParkCarOperation"/> <entity-group group="org.ofbiz" entity="ParkCarType"/> <entity-group group="org.ofbiz" entity="ParkInsExpireAlert"/> <entity-group group="org.ofbiz" entity="ParkPlace"/> <entity-group group="org.ofbiz" entity="Parking"/> </entitygroup>
The other file which follows describes each entity in its turn, I prefer to show a subset of the generated file regarding its length
<entitymodel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/entitymodel.xsd"> <title>Entity of the Neogia parking Component</title> <description>None</description> <copyright>Copyright (c) 2004, 2006 Neogia - www.neogia.org</copyright> <author>Misc</author> <version>1.0</version> <entity entity-name="DriveRecord" package-name="org.ofbiz.parking.manage"> <field name="sequenceId" type="name"></field> <field name="startDate" type="date-time"></field> <field name="endDate" type="date-time"></field> <field name="obsOut" type="name"></field> <field name="obsIn" type="name"></field> <field name="parkCarSequenceId" type="name"></field> <field name="partyId" type="id-ne"></field> <prim-key field="sequenceId"/> <relation type="one" fk-name="DRIVERECORD0" rel-entity-name="ParkCar"> <key-map field-name="parkCarSequenceId" rel-field-name="sequenceId"/> </relation> <relation type="one" fk-name="DRIVERECORD1" rel-entity-name="Party"> <key-map field-name="partyId"/> </relation> <value-factory class-name="org.ofbiz.parking.manage.developed.DriveRecordServices" /> </entity> <entity entity-name="OperationType" package-name="org.ofbiz.parking.manage"> <field name="opId" type="name"></field> <field name="description" type="name"></field> <field name="parkCarOperationSequenceId" type="name"></field> <prim-key field="opId"/> <relation type="one" fk-name="OPERATIONTYPE0" rel-entity-name="ParkCarOperation"> <key-map field-name="parkCarOperationSequenceId" rel-field-name="sequenceId"/> </relation> <value-factory class-name="org.ofbiz.parking.manage.developed.OperationTypeServices" /> </entity> <entity entity-name="ParkCar" package-name="org.ofbiz.parking.manage"> <field name="sequenceId" type="name"></field> <field name="number" type="name"></field> <field name="description" type="name"></field> <field name="kilometer" type="name"></field> <field name="observation" type="name"></field> <field name="oldNumber" type="name"></field> <field name="fiscalPower" type="name"></field> <field name="serialNumber" type="name"></field> <field name="circulationDate" type="date-time"></field> <field name="placeSequenceId" type="name"></field> <field name="parkingSequenceId" type="name"></field> <field name="parkCarTypeCarTypId" type="name"></field> <field name="parkCarEnergyEngyId" type="name"></field> <prim-key field="sequenceId"/> <relation type="one" fk-name="PARKCAR0" title="place" rel-entity-name="ParkPlace"> <key-map field-name="placeSequenceId" rel-field-name="sequenceId"/> </relation> <relation type="one" fk-name="PARKCAR1" rel-entity-name="Parking"> <key-map field-name="parkingSequenceId" rel-field-name="sequenceId"/> </relation> <relation type="one" fk-name="PARKCAR2" rel-entity-name="ParkCarKind"> </relation> <relation type="one" fk-name="PARKCAR3" rel-entity-name="ParkCarMark"> </relation> <relation type="one" fk-name="PARKCAR4" rel-entity-name="ParkCarType"> <key-map field-name="parkCarTypeCarTypId" rel-field-name="carTypId"/> </relation> <relation type="one" fk-name="PARKCAR5" rel-entity-name="ParkCarEnergy"> <key-map field-name="parkCarEnergyEngyId" rel-field-name="engyId"/> </relation> <value-factory class-name="org.ofbiz.parking.manage.developed.ParkCarServices" /> </entity> <entity entity-name="ParkCarEnergy" package-name="org.ofbiz.parking.manage"> <field name="engyId" type="name"></field> <field name="energy" type="name"></field> <prim-key field="engyId"/> <value-factory class-name="org.ofbiz.parking.manage.developed.ParkCarEnergyServices" /> </entity> <entity entity-name="ParkCarInsurance" package-name="org.ofbiz.parking.manage"> <field name="sequenceId" type="name"></field> <field name="effectDate" type="date-time"></field> <field name="expireDate" type="date-time"></field> <field name="agency" type="name"></field> <field name="number" type="name"></field> <field name="parkCarSequenceId" type="name"></field> <prim-key field="sequenceId"/> <relation type="one" fk-name="PARKCARINSURANC0" rel-entity-name="ParkCar"> <key-map field-name="parkCarSequenceId" rel-field-name="sequenceId"/> </relation> <relation type="many" rel-entity-name="ParkInsExpireAlert"> <key-map field-name="sequenceId" rel-field-name="parkCarInsuranceSequenceId"/> </relation> <value-factory class-name="org.ofbiz.parking.manage.developed.ParkCarInsuranceServices" /> </entity> ...............
I will not explain any of the XML elements that constitutes the file, as they are self-explanatory to any one familiar to the entity relationship data modeling, and because it out of the scope of this tutorial.
servicedef files
The following file "services.xml" list all services provided by the Parking component. You'll notice that the service generator doesn't make use of the mini-lang, all services are of type java. hopefully we didn't write any line of code, neogia generators took charge of this.
<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/services.xsd"> <service name="editDriveRecord" default-entity-name="DriveRecord" engine="java" location="org.ofbiz.parking.manage.generated.DriveRecordServices" invoke="editDriveRecord" auth="true"> <description>Create or Update a DriveRecord</description> <auto-attributes include="all" mode="INOUT" optional="true"/> <attribute name="driveRecord" type="org.ofbiz.parking.manage.developed.DriveRecord" mode="INOUT" optional="true"/> <attribute name="actionForm" type="String" mode="IN" optional="false"/> </service> <service name="removeDriveRecord" default-entity-name="DriveRecord" engine="java" location="org.ofbiz.parking.manage.generated.DriveRecordServices" invoke="removeDriveRecord" auth="true"> <description>Remove a DriveRecord</description> <auto-attributes include="pk" mode="IN" optional="true"/> <attribute name="driveRecord" type="org.ofbiz.parking.manage.developed.DriveRecord" mode="IN" optional="true"/> </service> <service name="editParkCar" default-entity-name="ParkCar" engine="java" location="org.ofbiz.parking.manage.generated.ParkCarServices" invoke="editParkCar" auth="true"> <description>Create or Update a ParkCar</description> <auto-attributes include="all" mode="INOUT" optional="true"/> <attribute name="parkCar" type="org.ofbiz.parking.manage.developed.ParkCar" mode="INOUT" optional="true"/> <attribute name="actionForm" type="String" mode="IN" optional="false"/> </service> <service name="removeParkCar" default-entity-name="ParkCar" engine="java" location="org.ofbiz.parking.manage.generated.ParkCarServices" invoke="removeParkCar" auth="true"> <description>Remove a ParkCar</description> <auto-attributes include="pk" mode="IN" optional="true"/> <attribute name="parkCar" type="org.ofbiz.parking.manage.developed.ParkCar" mode="IN" optional="true"/> </service> <service name="editParkCarInsurance" default-entity-name="ParkCarInsurance" engine="java" location="org.ofbiz.parking.manage.generated.ParkCarInsuranceServices" invoke="editParkCarInsurance" auth="true"> <description>Create or Update a ParkCarInsurance</description> <auto-attributes include="all" mode="INOUT" optional="true"/> <attribute name="parkCarInsurance" type="org.ofbiz.parking.manage.developed.ParkCarInsurance" mode="INOUT" optional="true"/> <attribute name="actionForm" type="String" mode="IN" optional="false"/> </service> <service name="removeParkCarInsurance" default-entity-name="ParkCarInsurance" engine="java" location="org.ofbiz.parking.manage.generated.ParkCarInsuranceServices" invoke="removeParkCarInsurance" auth="true"> <description>Remove a ParkCarInsurance</description> <auto-attributes include="pk" mode="IN" optional="true"/> <attribute name="parkCarInsurance" type="org.ofbiz.parking.manage.developed.ParkCarInsurance" mode="IN" optional="true"/> </service> <service name="editParkCarOperation" default-entity-name="ParkCarOperation" engine="java" location="org.ofbiz.parking.manage.generated.ParkCarOperationServices" invoke="editParkCarOperation" auth="true"> <description>Create or Update a ParkCarOperation</description> <auto-attributes include="all" mode="INOUT" optional="true"/> <attribute name="parkCarOperation" type="org.ofbiz.parking.manage.developed.ParkCarOperation" mode="INOUT" optional="true"/> <attribute name="actionForm" type="String" mode="IN" optional="false"/> </service> <service name="removeParkCarOperation" default-entity-name="ParkCarOperation" engine="java" location="org.ofbiz.parking.manage.generated.ParkCarOperationServices" invoke="removeParkCarOperation" auth="true"> <description>Remove a ParkCarOperation</description> <auto-attributes include="pk" mode="IN" optional="true"/> <attribute name="parkCarOperation" type="org.ofbiz.parking.manage.developed.ParkCarOperation" mode="IN" optional="true"/> </service> <service name="editParking" default-entity-name="Parking" engine="java" location="org.ofbiz.parking.manage.generated.ParkingServices" invoke="editParking" auth="true"> <description>Create or Update a Parking</description> <auto-attributes include="all" mode="INOUT" optional="true"/> <attribute name="parking" type="org.ofbiz.parking.manage.developed.Parking" mode="INOUT" optional="true"/> <attribute name="actionForm" type="String" mode="IN" optional="false"/> </service> <service name="removeParking" default-entity-name="Parking" engine="java" location="org.ofbiz.parking.manage.generated.ParkingServices" invoke="removeParking" auth="true"> <description>Remove a Parking</description> <auto-attributes include="pk" mode="IN" optional="true"/> <attribute name="parking" type="org.ofbiz.parking.manage.developed.Parking" mode="IN" optional="true"/> </service> </services>
data files
By default the neogia generators generate a file containing data about component user roles and their access rights. Actually I couldn't access my screens when the component became ready until importing the 'ParkingData.xml' data file using ofbiz webtools.
The inconvenient point here is that the file is suffixed with 'G' letter (surely to mean Generated). It's tiring to copy each time a generated file and then rename it before we can use it. Of course we can automate this with ant but there still will be some extra work on making the build.xml ready.
<entity-engine-xml> <!-- Standard generated permission for the package --> <SecurityGroup groupId="PARKINGADMIN" description="parking"/> <SecurityPermission permissionId="PARKING_ADMIN" description="parking Full Administration Permission"/> <SecurityPermission permissionId="PARKING_VIEW" description="parking Full View Permission"/> <SecurityPermission permissionId="PARKING_UPDATE" description="parking Full Update Permission"/> <SecurityGroupPermission groupId="PARKINGADMIN" permissionId="PARKING_ADMIN"/> <SecurityGroupPermission groupId="PARKINGADMIN" permissionId="PARKING_VIEW"/> <SecurityGroupPermission groupId="PARKINGADMIN" permissionId="PARKING_UPDATE"/> <!-- End of Standard generated permission for the package --> <!-- Standard generated permission for full admin --> <SecurityGroupPermission groupId="FULLADMIN" permissionId="PARKING_ADMIN"/> <SecurityGroupPermission groupId="FULLADMIN" permissionId="PARKING_VIEW"/> <!-- End of Standard generated permission for full admin --> <!-- Generated of Status and Enumeration --> <!-- End of Generated of Status and Enumeration --> </entity-engine-xml>
ofbiz-component.xml
I couldn't find this file, so I did it the classical way, took the quality neogia component (actually i used this component since it is small compared to other ofbiz-neogia components) and customized it.
<ofbiz-component name="parking"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/ofbiz-component.xsd">
<resource-loader name="main" type="component"/>
<!-- load single or multiple external libraries -->
<!--<classpath type="jar" location="lib/*"/>-->
<classpath type="jar" location="build/lib/*"/>
<!-- place the config directory on the classpath to access configuration files -->
<classpath type="dir" location="config"/>
<classpath type="dir" location="script"/>
<!-- entity resources: model(s), eca(s) and group definitions -->
<entity-resource type="model" reader-name="main" loader="main" location="entitydef/entitymodel.xml"/>
<entity-resource type="group" reader-name="main" loader="main" location="entitydef/entitygroup.xml"/>
<entity-resource type="data" reader-name="seed" loader="main" location="data/ParkingData.xml"/>
<!-- <entity-resource type="data" reader-name="demo" loader="main" location="data/DemoQualityData.xml"/>-->
<!-- service resources: model(s) [definitions], eca(s) and group definitions -->
<service-resource type="model" loader="main" location="servicedef/generated/services.xml"/>
<service-resource type="model" loader="main" location="servicedef/developed/services.xml"/>
<!-- <service-resource type="eca" loader="main" location="servicedef/secas.xml"/> -->
<!-- web applications; will be mounted when using the embedded Jetty container -->
<webapp name="parking"
title="Parking"
server="default-server"
location="webapp/parking"
base-permission="PARKING"
mount-point="/parking"/>
</ofbiz-component>
objet : Code generation presentation author : multiple reference page language : English


