Gregari’s persistence layer consists of the following:
- Database schema
- Value objects written as EJB3 Entity objects (mapped to the database via Java Persistence/Hibernate 3 annotations)
- DAO (Data Access Objects) written as EJB3 SSB (Stateless Session Beans)
Value Objects - The value objects are generated using the Castor XML open source project and XML Schemas. This was done as its relatively quick to model objects using a single file (XML Schema) and generate the following:
- classes
- attributes
- attribute getters/setters
- XML marshal/unmarshal code
- simple field validation
- etc.
Castor XML – Using Castor XML and XSD’s (XML Schema files) XML elements are mapped to Java classes, XML attributes are mapped to Java properties, XML element relationships (with other XML elements) are mapped to collections of objects (1:1 or 1:n depending the design). So if your familiar with XML Schemas… Gregari is taking full advantage of the XSD specifications. Castor XML is merely taking the XSD’s and generating Java classes. The normal Castor XML configuration is leveraged, i.e.
- Mapping XML namespaces to Java packages
- configuring which type of collections to be used
- create a “hashcode” method (true)
- create an “equals” method (true)
- inherit from a common base class
- etc.
All of this can be seen in the castorbuilder.properties file specification from Castor XML
Components - From a modeling perspective … there’s components (logical groupings of objects). In Gregari a single XML Schema is mapped 1:1 with a component. Within the XSD file are the component’s objects as well as relationships to each object (where applicable). Furthermore by using XDoclet2 tags within the XSD description element, the XDoclet annotations will be generated in the class, attribute and/or method JavaDoc comment sections. XDoclet annotations that are used include those for Hibernate as well as some Gregari-custom XDoclet tags. By adding the XDoclet tags… a post processor (custom written leveraging XDoclet’s parsing capabilities) will transform the Castor generated JavaBeans (POJO’s) into EJB3 Entity Beans complete with annotations (both EJB, Java Persistence, Hibernate ORM, Hibernate Search, Hibernate Validations, JBoss Seam, as well as some Gregari specific Java5 Annotations). In addition to the Gregari XDoclet post-processor (that transforms the POJO’s into EJB3 Entities), Hibernate Tools is used to generate Hibernate configuration/mapping files as well as generate database specific DDL SQL.
Build Process - To generate value objects… the build process has the following steps:
- From XSD files generate Java POJO’s using Castor XML Code Generator
- From POJO’s transform into EJB3 Entities using a Gregari custom XDoclet plugin
- From EJB3 Entities generate Hibernate configuration/mapping files (to generate DB specific DDL SQL) using Hibernate Tools
- From Hibernate configuration/mapping files generate DB specific DDL SQL using Hibernate Tools
Gregari Schema component – All of this can be found in the Gregari Schema component. Child components include the following (their individual mapping to databases is explained below while the database strategy is explained in the Database Strategy blog entry).
- Common – Contains common XSD files used to generate common POJO’s.
- Activity – Contains XSD’s specific to the user interface, i.e. metadata signifying what’s present as well as what can/cannot be customized by an application tenant. These objects are associated with the Metadata database.
- Audit – Contains XSD’s specific to any objects used to track system usage, i.e. user sessions, user request, user authentication/authorization attempts, data accesses, debug statements, scheduled job audits, etc. These objects are associated with the Audit database.
- Database – Contains any DDL SQL as well as DbUnit files used to create test data for any unit tests. There are no POJO objects within this component.
- Etc – Contains XSD’s that specific XML document root elements, i.e. placed in a common place. Not all root elements are here.
- Instance – Contains XSD’s specific to any objects used to create multi-tenant data, i.e. notes, addresses, UDF’s, etc. Presumably this could be placed in a single tenant shard or kept together in a multi-tenant shard. These objects are associated with the Instance database.
- Metadata – Contains XSD’s specific to signifying what’s present in the system as well as what can/cannot be customized by an application tenant. Also keeps track of any customizations. These objects are associated with the Metadata database as explained in the Database Strategy blog entry.
- Reporting – Contains XSD’s specific to the reporting component, i.e. metadata signifying what report categories, reports and report parameters are present. These objects are associated with the Metadata database.
- Scheduler – Contains XSD’s specific to the scheduler component, i.e. metadata signifying what job categories, jobs, scheduled jobs are present. These objects are associated with the Metadata database.
EJB3 Entity Final Notes - As noted earlier the EJB3 Entities are just basically POJO’s with getter/setter methods, XML marshal/unmarshal methods, and simple field validation. There is no complex validation nor business logic within these objects as they are generated. However, they are heavily annotated with Java5 Annotations to trigger behavior via Interceptors, etc. Furthermore the value objects have no understanding of an underlying persistence mechanism, i.e. they could be mapped against a database or marshaled into XML and sent to an ESB. Any persistence via a database or an ESB would need to be handled in other objects, i.e. DAO’s, etc.
DAO (Data Access Objects) - DAOs wrap the interaction between the value object and the persistence mechanism’s (in this case Hibernate). Normally you create a well defined interface and then whatever concrete implementations. So you could swap out implementations as needed (i.e. talk to the database, or LDAP, or an ESB). That’s the theory at least. So in these DAO’s an javax.persistence.EntityManager is injected. This EntityManager is tied with a javax.persistence.PersistenceContext. The PersistenceContext in turn is tied with an underlying database. For Gregari only one EntityManager/PersistenceContext is injected per DAO (this helps as there are multiple databases and want to make sure the right object goes into the right database. The layer above (i.e. BO or Business Object layer) handles the orchestration of multiple DAO’s in a given unit of work. The BO object is not tied to a specific database, rather it can work with multiple DAO/EntityManager/PersistenceContext tuples.
The DAO’s typically have the following methods:
- get a list of objects (given any parent ID’s as well as a QBE (query by example) search pattern)
- get a specific object based on its ID
- get a specific object based on its business key (code, tenant ID, etc)
- insert an object
- update a specific object (for UI’s that drill down into the object details and potentially update its contents)
- update a set of objects (for UI’s, etc. that support bulk updates)
- delete a set of objects (for UI’s, etc. that allow multiple deletes)
Generally speaking this is set up for your normal QBE Search (Query By Example) as well as CRUD operations (Create, Read, Update, Delete). Very specific searches (get this object’s children that are active) can be placed in the BO (with the QBE parameters hardcoded) thus reusing the “get a list of objects” DAO method. Where reuse doesn’t make sense then create new BO/DAO methods to handle the query. But ideally the BO objects have far more methods to handle specific business logic than the DAO’s. If the DAO’s have a lot of methods then… you may need to reconsider your model. In addition the DAO’s should be reusable across BO’s (sure there might be a BO 1:1 mapping with DAO’s to do CRUD type business logic. But for more complex business logic… the BO might need multiple DAO’s.
Annotations – The DAO objects don’t have a lot of Java 5 annotations, currently just EJB3 Stateless Session annotations, JBoss Seam annotations and some Azzura Gregari specific annotations for checking that the right tenant’s users are seeing the right data. The annotations take into account the following concepts, i.e.
- Provider data – Data that only the SAAS provider should see.
- Global data – Data that any tenant can see, i.e. metadata regarding the system (entities, attributes/methods/relationships on the entities, etc)
- GlobalLocal data – Data that normally is global but can be overridden by a particular tenant. Overridden data should only be visible to the tenant whereas global data should be available to all tenants. Essentially there could be a mixture of global and local data within the same result set (typically metadata). An example might be for a status attribute enumeration, i.e. global values would be “active” or “inactive”. But a tenant might have added “deleted” to satisfy one of their internal needs.
- Local data – Data that should only be visible to the tenant.
In subsequent blogs I’ll describe the service layer, add some diagrams and code snippets.