A module is a collection of Java classes implementing de.ecm4u.faw.action.Action,
Form or Action JSON, Textresources, and other resources that the FaW server can
read from the Java classpath. It also contains JSON representations of
database rows that have to be inserted into the database.
Every module has a name and a version. Only one version of a module can be installed into a FaW server although a version can be upgraded by installing a higher version.
When the FaW server starts the first for the first time after installing a version of a module by putting it on the classpath, the FaW server will recognize this and import the JSON represenations into the database. This can be used to insert new or update existing database rows like forms, tasks, actions, or textresources. This importing is only done once. Further restarts of the FaW server will recognize that the module version has already been imported and do nothing.
The JSON representation of database records uses UUIDs to identify rows in a portable way. Foreign keys are expressed using UUIDs, too.
Each database row must have a UUID which must not be changed. If the import logic encounters th JSON representation of a database row with an unknown UUID, it will insert the row into the database. If the UUID is alread known, the corresponding database row will be updated using the JSON represenation.
Upon first inserting a database row based on a JSON representation, the
timestamp column of the database row will be set to the current timestamp.
This applies to Tasks, Forms, and Textresources. The timestamp is used to decide
if the row is old enough for a Flow to use. Only a row that is not
newer than the creation timestamp of the Flow will be used. This
prevents incompatible changes to affect existing Flow and Task. It
guarantees that old instances will continue to use Tasks, Forms, and Textresources
that where current at the time of their creation.
Upgrading a module to a newer version will never change the timestamps. If an incompatible change to a Task, Form, or Textresource must be made, a new JSON representation for it must be created and imported. The resulting row will receive the import time as its timestamp value. Only Flows and Tasks created after this import time will use it. Older instances will continue to use older versions.
If you want to develop a new module, you should use the Module Build Tool. This tool simplifies the process of building a module.
A module is a JAR file with no dependecies.
The Tasks that make up a module are defined in a YAML file called moduleConfig.yml.
A module can contain any number of JSON files with representation of database rows. The simplest file is just an empty JSON object:
{}
It can contain objects for the types of database tables, all of them are optional. A valid but technically empty JSON file would be:
{
"tasks": {},
"actions": {},
"actionPredecessors": {},
"actionScripts": {},
"forms": {},
"textresources": {}
}
The objects keyed by tasks, actions, etc. there can be any number of
JSON object representing a database row of the respective table. Each object
is keyed by the UUID of the row. It contains fields that match the non-technical
columns of the database table.
Example:
{
"textresources": {
"b1c6cfee-ec6f-4c31-9ccf-14b44319f13c": {
"name": "sample.txt",
"type": "text",
"method": "inline",
"value": "sample inline textresource",
"module": "sample-1.0.8"
}
}
}
This represents a single row in the textresource table. Note that not for all
columns values are given. Especially the id, deleted, uuid, and timestamp
(where existing) columns must not be set in a JSON representation. The id,
deleted, and timestamp are set by the server, the uuid column is defined
by the JSON key.
module PropertyEvery JSON object representing a row must have the property module. This is
used to identify database rows that where imported from the same version of
the same module.
It is recommended to set the module property to the same value for all JSON
objects in a JSON file. It is further recommended to name the JSON file to
match the module property used.
Example: The above textresource should be put into a file named sample-1.0.8.
Some tables have foreign key relations to other tables. The foreign keys are expressed using the UUIDs. For example, a task has actions:
{
"tasks": {
"7b8306e9-9945-4996-90e8-b7c5dcf588cc": {
"name": "Eingangsdokument erfassen",
"module": "sample-1.0.9"
}
},
"actions": {
"a1daac4c-d014-4bd1-a772-5eff48b7b877": {
"task_uuid": "7b8306e9-9945-4996-90e8-b7c5dcf588cc",
"name": "05-AssignAuthority",
"implementation": "de.ecm4u.faw.api.impl.AssignAuthorityAction",
"creation_order": 5,
"params": "classpath:/de/ecm4u/faw/module/collectandstore/actions/05-AssignAuthority-1.2.12.json",
"module": "sample-1.0.9"
},
"12d4a00d-a3bb-485d-a83c-d1d6404b394f": {
"task_uuid": "7b8306e9-9945-4996-90e8-b7c5dcf588cc",
"name": "10-CreateResource",
"implementation": "de.ecm4u.faw.api.impl.CreateResourceAction",
"creation_order": 10,
"params": "{}",
"module": "sample-1.0.9"
}
}
}
Note that the UUID of the task 7b8306e9-9945-4996-90e8-b7c5dcf588cc is
referenced by the actions in the task_uuid field. The server will insert
the proper foreign key relations when inserting or updating the database rows.
The same is done for the foreign key relation from actionPredecessors to
actions.
Forms, actions, and textresources can be included inline in the JSON objects as text or as files in the module.
A form is defined in the definition JSON property. This property can contain
the JSON definition of the form or it can reference a classpath resource.
Example 1:
{
"forms": {
"45ab69cc-15d5-4a54-b13f-61fb6d9b3829": {
"name": "Pay Supplier Invoice",
"definition": "{\"schema\": { \"properties\": { ... } }, ... }",
"module": "sbcs-collect-and-store-1.2.7"
}
}
}
It is obvious that quotes in the inlined JSON must be escaped and that there can
be no linebreaks to make it more readable. It is recommended to define
the form in a JSON file and reference it in the definition property.
Example 2:
{
"forms": {
"45ab69cc-15d5-4a54-b13f-61fb6d9b3829": {
"name": "Pay Supplier Invoice",
"definition": "classpath:/de/ecm4u/faw/module/collectandstore/forms/Pay-Supplier-Invoice-1.2.7.json",
"module": "sbcs-collect-and-store-1.2.7"
}
}
}
The leading classpath: is recognized at runtime and the form is loaded from
the JSON file in the module. The file Pay-Supplier-Invoice-1.2.7.json must
be saved as
/sr/main/resources/de/ecm4u/faw/module/collectandstore/forms/Pay-Supplier-Invoice-1.2.7.json
if Maven is used to build the module JAR.
The parameters of an action are defined as a JSON object in the params
property. As for forms, this property can be inlined or referenced as a file
on the classpath.
Example 1:
{
"actions": {
"f387b0fe-e453-4338-9a52-3bc8a6a23aa5": {
"task_uuid": "b58d3600-2398-49b0-be36-a4fd5d03183b",
"name": "05-ReviewSupplierInvoice-AssignAuhtority",
"implementation": "de.ecm4u.faw.api.impl.AssignAuthorityAction",
"creation_order": 5,
"params": "{ \"AssignAuthorityAction.assignee\": \"$authority\", \"AssignAuthorityAction.watcher\": \"$watcher\"}",
"module": "sbcs-collect-and-store-1.2.7"
}
}
}
Here the params property contains the action parameters as a JSON object
that must be escaped. It is recommended to define the action parameters
in a JSON file and reference it in the params property.
Example 2:
{
"actions": {
"a1daac4c-d014-4bd1-a772-5eff48b7b877": {
"task_uuid": "7b8306e9-9945-4996-90e8-b7c5dcf588cc",
"name": "05-AssignAuthority",
"implementation": "de.ecm4u.faw.api.impl.AssignAuthorityAction",
"creation_order": 5,
"params": "classpath:/de/ecm4u/faw/module/collectandstore/actions/05-AssignAuthority-1.2.12.json",
"module": "sbcs-collect-and-store-1.2.7"
}
}
}
The leading classpath: is recognized at runtime and the action parameters
are loaded from the JSON file in the module. The file 05-AssignAuthority-1.2.12.json
must be saved as
/sr/main/resources/de/ecm4u/faw/module/collectandstore/actions/05-AssignAuthority-1.2.12.json
if Maven is used to build the module JAR.
Textresources can be inlined or loaded from the classpath or a local file. Loading a textresource from a local file is useful for configuration files that have to changed by the administrator. For textresources included in a module inlining them or loading them from the classpath is recommended.
Example 1:
{
"textresources": {
"a02a6822-1e67-4c71-a91a-16b6562333ad": {
"name": "executed action",
"type": "template",
"method": "inline",
"value": "${user} executed action '${action}' on this task",
"module": "faw-base-1.0.1"
}
}
}
This textresource has the method inline, the value property contains
the complete text.
Example 2:
{
"textresources": {
"c8deac78-5548-4ff8-8f86-fbe3b3e1e663": {
"name": "ecm4u-lib.js",
"type": "lib",
"method": "url",
"value": "classpath:/textresources/scripts/lib/ecm4u-lib.js",
"module": "faw-base-1.0.0"
}
}
}
This textresource has the method url. The classpath: prefix in the value
indicates that its text has to be read from the file
ecm4u-lib.js on the classpath.
Upgrading a module requires incrementing the version. The FaW server will only
import the JSON representations included in a module if it hasn't done so. To
make the FaW server import a new version, the version parameter of the
@Module annotation must be incremented.
The version property of the Maven module should be incremented accordingly.
There are two kind of changes that can happen in an upgraded module: compatible and incompatible changes.
Compatible changes are changes that will not prevent existing tasks and flows to continue. Examples are bugfixes in Nashorn scripts, cosmetic changes to form layouts, added I18 labels. As soon as a change will break existing tasks or flows it is incompatible.
A compatible change can be made to an existing classpath resource. When the upgraded module is deployed, the FaW server will load the changed classpath resource at runtime.
JSON representations can also be changed, for example fixing the typo in an inlined form, action, or textresource. The FaW server will update the existing database rows using the updated JSON object. To identify existing rows the UUID is used. Note that the timestamp of an updated row is not changed. It is left at the value inserted at the time of the insertion of the row.
The names of JSON representation files changed only in compatible ways should
not be changed. So even if the module has a higher version, for example 1.0.2,
the name of an existing JSON representation file should still include the old
version, for example 1.0.1. In the importFiles attribute of the @Module
annotation the name of the JSON representation file must also be left unchanged.
Incompatible changes are changes that, if used with or applied to existing tasks and flows, will break them. Examples are new form fields that scripts now depend on or changed logic in a Nashorn script that will not work with a task or form that already exists.
An incompatible change must not be made to an existing classpath resource. Instead, a copy of the classpath resource should be made and put onto the classpath using a new name.
Depending on the type of changed classpath resource, a new JSON representation
must be included. For this a new JSON representation file should be created
and added to the importFiles attribute of the @Module@ annotation.
Only add new JSON representations to such a new file using new UUIDs. If classpath resources are referenced by such a new JSON representation, only reference to new classpath resources added together with the JSON representation.
Never remove anything from a module. Keep all classpath resources and all JSON representations. This way the module will always include everything necessary for all tasks and flows created at various times in the past.
Version numbers follow Sematic Versioning. They are used in the following way.
The name of a JSON representation file should include the version that was current when the file was first added. It should not be changed later.
module Property of JSON RepresentationsThe module property of JSON representation shall be build using the module
name and the version that was current when the JSON representation was first
added. It should not be changed later.
This and the previouse rule will effect in the name of the JSON representation
file and all module properties in it being identical.
The filename of a classpath resources shall include the version that was current when the classpath resource was first added. It should not be changed later.
This and the previous two rules effect in the version in the classpath references in the JSON representations, the classpath resource filenames, and the name of the JSON respresentation file being identical.
Put a Module JAR into the directory /usr/lib/ecm4u/sbcs-faw. Restart the server.
A Module JAR can be unpacked into the directory /usr/lib/ecm4u/sbcs-faw-extension.
All classpath resources (scripts, JSON patches, ...) can be directly edited in the
file system.
Make sure to include all changes made into the project that builds the JAR.