. . .

Development Basics

This page gives you a short introduction about the basics of developing modules with ApiOmat. You'll get a simple introduction of the development workflow and a brief overview about the relevant parts of the Native Module SDK. The links which are contained on this page to further topics will help you to go deeper into module development.

Contents :


In general, you can achieve a new Native Module in five basic steps:

  1. Create a new module and the classes via Dashboard

  2. Download the Native Module SDK or check out the Native Module code from the Git repository (depending on how you created the module)

  3. Import the SDK into your development environment

  4. Insert the logic to be executed in the backend by using hook classes.

  5. Upload the modified module / push the changes to the Git repository (depending on how you created the module)

About the Native Module SDK

As the above list guides you to a working Native Module, there are some further technical aspects you should know about.

Package structure

The package com.apiomat.nativemodules includes all necessary Java classes. You can find interfaces, annotations and utility classes that are needed for execution at this package level .

The package com.apiomat.nativemodule.basics contains the classes User and Role, which do exist in each module. The class User represents a user of an app, which can be used to authorize. The class Role describes a role that a user of your app can take. You can restrict the access to all objects of specific classes (class level access control) or even directly to specific objects (object level access control). You can find further information about this topic on our page about data access security .

You can find the non-generic classes of your module on the same level as the basics package. The full package name is always com.apiomat.nativemodule.<modulename> where the <modulename> is always the lower-cased name of your module. You can find more information about the classes that are contained there in the chapter about "user-defined classes" below.

Another package is com.apiomat.nativemodules.interfaces.dmap, which contains classes to handle distributed maps.

ApiOmat generates a bunch of packages and classes which can be adjusted by you. It is also still possible for you to benefit from common architecture patterns, as you would do in ordinary desktop development. You can put additional packages and classes in the src folder and call them from within the hook methods. ApiOmat can then access the functionality after uploading.


The properties file sdk.properties holds all data that is necessary to automatically up- or download an updated SDK containing changes to the class structure to or from ApiOmat.

You have to adjust the following values initially:

  • password - the password of the module owner

  • merge - "true" (default), if the downloaded package should not only include generated classes and utility classes, but also customized and self-made classes. (e.g. hooks, REST Endpoints, ..)

  • update - "true", if local changes to model classes should be recognized by ApiOmat and thus be reflected in the class structure
    - "overwrite"(default), if an attribute has been modified or deleted locally and these changes should be repeated to the server
    - "false", for none of the above (or just comment out the update setting)


The folder lib contains all libraries that are necessary for this project. They include libraries like "swagger-annotations" for the REST endpoints and other ApiOmat modules, as well as the currently developed module uses other modules (e.g. the SAML module for SSO authentication).

You can add other libraries that are neccessary for the module here as well. You should note, that libraries which are already included in YAMBAS (e.g. Apache HTTP Client) must have the same version number as the corresponding YAMBAS libraries!

You can retrieve a list of all libraries that YAMBAS uses through calling <BasisURL>/yambas/rest with authentication credentials of the Super Admin, for example:

curl http://localhost:8080/yambas/rest -u apinaut@apiomat.com:secret

Module main class

First of all, there is a module main class with the same name as the module. It is always located in the package com.apiomat.nativemodule.<modulename> and must be provided to upload a module.

This class provides information about the module itself (like the description), the configuration of the module, methods that handle the lifecycle of a module and contained fields over which you can invoke module-specific methods (like logging or handling of objects).

Module Annotation

The module main class must contain the Module annotation as class annotation. It indicates YAMBAS (beside the mentioned naming convention) that this file is the main class of the module.

The annotation holds additional information about the module, like the description, the category of the module, the documentation URL, all modules that this module uses and the security permissions this module needs (which is only necessary if you're running a white label installation).

If you specify a module description, category and/or documentation, these information will be displayed for example in the module market of the Dashboard. If you add your module to a backend and your module uses other modules that were not already added to the backend before, ApiOmat will automatically append these modules to your backend, too.

Example for a module annotation
@Module( description="Example Module", category ="Communication", documentationURL = "/docs/example-module.html", usedModules={"AnotherExampleModule", "SMTP"}, securityPermissions = {} )

Static helper fields

Each module main class contains two static fields by default.

The first field is of the type IStaticMethods and is named AOM. It is the main entrance point to handle objects. It provides methods to create objects, find objects by its id, foreignId, query or access token. It allows you to log information or errors, lets you throw exceptions correctly or handle distributed maps. You will use this interface very frequently.

The second field is of the type IApplicationConfigProxy which is named APP_CONFIG_PROXY and allows access to the module configuration with its methods getConfigValue and getParsedConfigValue. Read the next paragraph to get more information about the module configuration.

Module Configuration

Of course it is possible to define configuration parameters for your own module. This is done by defining a static String variable and adding the NativeModuleConfig annotation in the module main class:

@NativeModuleConfig( datatype = Type.TEXT, example = "http://www.mywebsite.com/api", title = "URL", info = "URL endpoint my website", key = "url", order = 1, notifyAllNodes=false )
public static String URL = "mymodule_url";

NativeModuleConfig provides several data types, an example value, a title and a description for a specific configuration parameter. You can modify display order of the several parameters in the module configuration screen, as shown below, by specifying the order field.

The field notifyAllNodes can be used to indicate that a change of this configuration value will be reflected on all nodes of your ApiOmat cluster. By default, only the node where the request for the configuration change arrives will execute the onConfigChanged hook for this value (see next part).

As soon as the configuration parameters are added to the module main class and the module has been uploaded, the parameters are displayed by hovering over the module name in the left bar of the Dashboard until a context menu opens. There you can choose the "Configuration" entry to open the menu. Also, m odule configurations are specified each time the module is added to a backend using the Dashboard, as shown in this example of the configuration for the Evalanche module:


You can get the current value of a configuration entry of one backend in the module code by calling the getConfigValue method of the APP_CONFIG_PROXY field in the module main class:

<ModuleName>.APP_CONFIG_PROXY.getConfigValue( <ModuleName>.<ConfigName>, appName, system );

The getConfigValue method will return the value in that type, the value was set in the configuration of the backend (by default, the dashboard will store the values as String). As of ApiOmat 3.0, you can use the

<ModuleName>.APP_CONFIG_PROXY.getParsedConfigValue( <ModuleName>.<ConfigName>, appName, system );

method in your modules' code to get the value in that type, you specified in the NativeModuleConfig annotation. If you specified the type boolean, the method will try to parse the values "true" and "1" to true and "false" and "0" to false. If the type is set to "Number", String values containing a dot are returned as double and without a dot as long values. If the value cannot be parsed, it will return the unparsed value (just like calling getConfigValue).

Life cycle methods of a module

The main module initially contains the following methods which reflect hooks for events that occur during the life cycle of a module:

public void onDeploy( )
public void onUndeploy( )
public void onDeployForAppBackend( final String appName, final com.apiomat.nativemodule.Request request )
public void onUndeployForAppBackend( final String appName, final com.apiomat.nativemodule.Request request )
public void onConfigChanged( String appName, String configKey , String system)
public void onCronHourly( final String appName, final String system )
public void onCronDaily( final String appName, final String system )
public void onCronWeekly( final String appName, final String system )
public void onCronMonthly( final String appName, final String system )
public void onModuleAttach( final String appName, final com.apiomat.nativemodule.Request request )
public void onModuleDetach( final String appName, final com.apiomat.nativemodule.Request request )
public int checkHealth( final String appName, final String system )
public AbstractRestResource getSpecificRestResource( UriInfo uriInfo, HttpServletRequest servletRequest,SecurityContext securityContext, Request wsRequest )

Below explains each method and its purpose.

OnDeploy and onUndeploy
The onDeploy and onUndeploy methods are hooks which are called each time a module is deployed or undeployed. The onDeploy is called each time the module is uploaded using the upload or if its moduleStatus is manually changed to DEPLOYED . The corresponding onUndeploy will be executed if the status of the module is set to UNDEPLOYED . This method is also called if a redeploy (upload of a module again, as the old version of the module will be undeployed and the newly uploaded version is deployed ) is performed and is executed before the onDeploy takes place.

If your module contains some used modules the hook methods of the used modules will also be called hierarchically in the following manner:

  • The onDeploy hook methods of the used modules from lowest hierarchy level will be called before the hook methods of the modules on higher hierarchy levels.

  • The onUndeploy hook methods of the used modules from lowest hierarchy level will be called after the hook methods of the module on higher hierarchy level s.

Additionally, we recommend to clean your static fields within your deployment hook methods to avoid long living objects and memory leaks . See next subsection ' Notes about statics in module main ' for further details.

OnDeployForAppBackend and onUndeployForAppBackend
The hook methods onDeployForAppBackend and onUndeployForAppBackend are called for every backend that contains the module which gets deployed or undeployed. For instance: if your module is attached to ten backends and gets deployed, the onDeployForAppBackend will be called ten times for each backend. If ApiOmat runs within a cluster installation with multiple nodes, you also may define that those backend specific hook methods are invoked on every node. By default the hook methods onDeployForAppBackend and onUndeployForAppBackend are invoked on only one cluster node. Your implemented application specific hook methods are invoked in order on every cluster node. Update the Module attribute callAppBackendSpecificHooksOnAllNodes to true (default = false).
If your module contains some used modules the hook methods of the used modules will also be called hierarchically in the following manner:

  • The onDeployForAppBackend hook methods of the used modules from lowest hierarchy level will be called before the hook methods of the modules on higher hierarchy levels.

  • The onUndeployForAppBackend hook methods of the used modules from lowest hierarchy level will be called after the hook methods of the module on higher hierarchy level s.

Again, we recommend to clean your static fields within your deployment hook methods to avoid long living objects and memory leaks. See next subsection ' Notes about statics in module main ' for further details.
The onConfigChanged hook informs the module about changed configuration values in a backend. Depending on the notifyAllNodes flag in the NativeModuleConfig annotation (see the paragraph above) of the specific configuration value, this hook is either executed only on one or on all nodes of your ApiOmat cluster. This method is called on any change to the current configuration and receives the app name as well as the changed key through its parameter list. This means that onConfigChanged is called once for every changed configuration parameter.
OnCronHourly, onCronDaily, onCronWeekly and onCronMonthly
There are four methods where you can implement cron-like tasks. These methods are executed automatically at a specific time. Typically, things like periodic sync or cleanup tasks can be put in these methods. The methods are executed as follows:

  • onCronHourly: every full hour (e.g. 8pm, 9pm, 10pm,...)

  • onCronDaily: every day at 3am

  • onCronWeekly: every week on Mondays, 3am

  • onCronMonthly: on every first day of the month, 3am

For more information about the cron hook methods and custom cron methods visit Cron Jobs.

OnModuleAttach and OnModuleDetach
There is also an onModuleAttach hook which simply gets invoked, if your module is attached to a backend. The hook method onModuleDetach is invoked when your module is detached from any backend.

You can implement
health checks for your module, which can be called via REST; see more here.

You can specify a class that should receive custom REST requests in the method getSpecificRestResource. An example class for that is contained in every module with the name RestClass.java. Thus, it is possible to provide an entirely custom-tailored REST interface to implement OAuth callbacks for example. Please see this separate article to get more information.

Notes about statics in module main

Since the module main class can internally be instantiated multiple times, it should be noted that when using the key word "static". Static variables will share their values with every backend that uses the respective module. If a backend "A" changes a static variable of a module class, this new value will be visible and editable from backend "B", too.

This behaviour is not intended in most cases and can be avoided by storing values inside a map with the respective app names as keys. Instead of

static Connection con;

you could use

static Map<String, Connection> con;

User-defined classes

Any class that you define for your module through the Dashboard will generate a new corresponding Java class that you can retrieve by downloading the latest version of the native module SDK. The class will contain all attributes that you defined in the Dashboard. As mentioned above, these classes are located in the package com.apiomat.nativemodule.<modulename>.

Each of these model classes will have an Model annotation to indicate that the given class is an ApiOmat based model class. The model annotation holds information about the related hook-classes, the transient, global and invisible state of the class and it's allowed and required roles. Additionally you are able to define a role classes map for dynamic role checks.

It is also possible to add user defined interfaces to your model classes. Therefore simply add the Java files containing the interface(s) and add them to the implemented interfaces of your class.

Example for a Model annotation
@Model( moduleName = "TestModule",
hooksClassNameTransient = "com.apiomat.nativemodule.test23.TestClassHooksTransient",
hooksClassNameNonTransient = "com.apiomat.nativemodule.testmodule.TestClassHooksNonTransient",
isTransient = false, isGlobal = false, isInvisible = false,
requiredUserRoleCreate=UserRole.User, requiredUserRoleRead=UserRole.User,
requiredUserRoleWrite=UserRole.Owner, restrictResourceAccess=false,
allowedRolesCreate={"Role1","Role2"}, allowedRolesRead={"Role1","Role3"}, allowedRolesWrite={"Role3"}, allowedRolesGrant={},

This generated class cannot be changed although changes to the data model can be made through the Dashboard and downloaded afterwards using the native module download. Unchanged files will be overwritten while doing so. However, it is possible to add new attributes directly in the source code of a user-defined class. They will be added in ApiOmat automatically as soon as the updated source code is uploaded. The SDK can be downloaded afterwards if it isn't required to define getters and setters oneself since existing classes will be replaced by generated ones.

In order for changes in these classes to take effect after uploading, the update flag in the sdk.properties file must be set with true. If existing attributes are to be deleted or modified as well, the flag has to be set to overwrite. If you don't want any of this, set it to false or comment it out.

References can be annotated with the EmbeddedObject annotation to mark them as Embedded Object.

@EmbeddedObject private Address address = null;

Attributes of type Long, Double, String, and Date can also be annotated with a validation pattern. If you define a regular expression (regex), any value for this particular attribute will be matched against this expression. In case the matching fails, saving the object will be rejected. Next to this you can define a min and max length for an attribute.

Example for pattern annotation
@Pattern ( regex = "[0-9]*" )
private String phoneNumber = null;
@Size( min = 6, max = 99 )
private String password = null;

In addition, you can give more detailed information about the attributes usage by setting the description and example annotation

Example for description and example annotation
@Description ( value = "this field is used for the phone number of a user")
@Example( value = "034126422235")
private String phoneNumber = null;