This project allowes to programatically define, use and validate a generic typesystem consisting of types, attributes and items. Furthermore there a some services which may be customized to your own project needs. The main service to deal with is GenericTypesystemService. This acts as the entry point for all operations and connects the various features like validation and persistence.
Although this project was extracted from a single personal project the datamodel and services are designed to be extended. But to be honest datamodel customization is not really used by myself at the moment.
Just include the maven dependency provided via maven central:
<dependency>
<groupId>de.chrgroth.generic-typesystem</groupId>
<artifactId>generic-typesystem</artifactId>
<version>${latest version see maven central badge above}</version>
</dependency>
back to top.
The datamodel is defined using a handful of POJOs and enums contained in model. A type can define attributes of concrete type and with specific restrictions / configuration. Each item belongs to a type and contains the concrete values for defined attributes. Due to very loose coupling an item may also contain values for undefined attributes, but in case of item validation this situation will be detected.
back to top.
A type is defined using GenericType and extends GenericStructure. This allows recursive re-usage of structured attributes as kind of nested types.
Beside attributes a type is defined using the following properties:
- id: a unique numeric identifier
- name, group: human readable type name and optional group for organizational purposes
- owner, visibility: see Ownership & Visibility
- pageSize: optional default page size, see Persistence
- customProperties: optional java.util.Map of custom properties to be used for project based customization
back to top.
A type attribute is defined by the following properties:
- id: a unique numeric identifier (per type)
- name: human readable attribute name
- type, valueType, structure: the attribute type. In case of a list type valueType defines the list values type. In case of a structure type, structure defines the nested structure (that's basically a list of attributes) to be used.
- unique: whether the type value belongs to the unique key
- mandatory: whether the type value is mandatory
- min, max, step: defines the min, max and step values for numerical types. Textual types may use min and max in case of value length validation.
- pattern: an optional pattern to be validated against
- defaultValue, defaultValueCallback: optional default values and default value callback which may be invoked somwhere outside this framework on client side.
- valueProposalDependencies: a list of attribute ids considered for value proposal. See value proposals section under persistence
- unitsId: defines if the attribute references a units definition. See below for more information.
- enumValues: defines the valid enum values and must be used if and only if the attribute type is an enum type
- customProperties: optional java.util.Map of custom properties to be used for project based customization
Further more each attribute has a unique path in context of a type. The path is formed using the attribute name and a dot for nested structures. The path is relevant when working with items.
back to top.
Values may be unit based and the definition of units is a quite simple one at the moment:
- id: a unique numeric identifier
- name: a name
- description: an description
- units: definition of concrete units
Each unit is defined using the following data:
- id: a unique numeric identifier (per units)
- name: a name
- symbol: a symbol representation
- factor: the factor related to the base unit
Each units definition must contain a single unit using the base factor (1.0), also defined as constant in GenericUnits. Examples for unit definitions and used conversions can be found in the unit tests, e.g. GenericTypesystemServiceTest.
Conversion of units can be achieved using the corresponding method in GenericTypesystemService. See below for more information regarding services.
back to top.
An item is defined using GenericItem and is quite simple:
- id, typeId: unique item id and id of the defining type
- owner, visibility: see Ownership & Visibility
- values: all values are stored in a java.util.Map using the attributes path as key. This allows all clients to work with the more manageable attribute path instead of attributes id.
The values map needs to contain a suitable java type instance for the specified attribute type. In case of nested structures a nested instance of GenericItem is contained in the map.
Values belonging to unit based attributes are modeled using UnitValue instances, containing the value itself and a reference to the unique units name.
back to top.
The main service to handle generic typesystem is GenericTypesystemService. This service acts as main entry point and is composed using further sub-service instances for special purposes, i.e. persistence and validation. Later ones may easily be overwritten / exchanged, see also Persistence and Validation. Further sub-services may be introduces in future releases.
Thus GenericTypesystemService itself contains only a minimum amount of business logic and mainly prepares and composes or just delegates calls to defined sub-services.
Every public method takes an instance of GenericTypesystemContext. Currently the context is used to ensure Ownership & Visibility concepts, but may be enhanced in future releases. There are two default context implementations available you may choose:
See Ownership & Visibility for more details.
back to top.
Validation aspects are handled using a sub-service defined by ValidationService. Each validation results in a ValidationResult containing all validation error including the path the error occurred at and a message key which can be mapped to suite your own needs.
By default the DefaultValidationService is used. This service can be extended providing an implementation for DefaultValidationServiceHooks.
To completely disable validation NoValidationService can be used.
back to top.
Persistence is also separated to an own sub-service defined by PersistenceService. By default the InMemoryPersistenceService is used, meaning all data is hold in memory only and not persisted during JVM shutdowns ot even new service instantiations. This is a good starting point for prototyping but needs to be replaced for any of your projects going beyond this phase.
Apart from default CRUD operations for types and items there are some more concepts located in persistence service:
Both of the above topics are implemented as further sub-services and passed to the constructor of defaults in memory persistence service instance. The default implementations may also be reused by your own implementation of persistence service, if they meet your needs.
back to top.
The concept of value proposals calculates all currently existing values for all value proposal capable attributes for a given type. To take it further an optional template item of given type may be provided to reduce the possible values using only items with matching attribute values regarding all defined value proposal dependencies.
The default implementation works in memory only, so all items have to be loaded for this operation. if this is somehow acceptable for you, just reuse the implementation InMemoryValueProposalService.
back to top.
InMemoryItemsQueryService is the default implementation for querying purposes and also works in memory only. So again all items have to be loaded to use it. Querying contains the following features executed in declared order:
- filtering
- sorting
- paging
Unfortunately filtering is not implemented yet but will be provided (also in an in-memory only fashion) in a future release.
back to top.
Ownership and visibility concepts are used to restrict access to concrete type and item instances. An owner is represented as a Long, assuming to be a unique user id or something similar. Visibility is implemented as enum and may be public or private. Default implementation rules can be found in javadocs of GenericTypesystemContext.
If you do not want to use any ownership or visibility constraints, you may use an instance of NullGenericTypesystemContext or provide your own context implementation.
back to top.
back to top.