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 get deeper into module development.
Contents:
Workflow
In general you can achieve a new Native Module in five basic steps:
-
Download the Native Module SDK or check out the Native Module code from the Git repository (depending on how you created the module)
-
Import the SDK into your development environment
-
Insert the logic to be executed in the backend by using hook classes.
-
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 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 an 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 informations 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.
Module logo
If you take a look to the module market of the dashboard, you'll find a small identifying image for every module. To achieve that your module contains such an identifying image, y ou can place a file called logo.png to the src directory. You should ensure that the file contains a transparent background and measures 80px x 80px to be displayed correctly.
SDK-Properties
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)
Libraries
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 contains 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.
@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 if you hover 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
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
int
checkHealth(
final
String appName,
final
String system )
public
AbstractRestResource getSpecificRestResource( UriInfo uriInfo, HttpServletRequest servletRequest,SecurityContext securityContext, Request wsRequest )
We will next describe 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.
OnConfigChanged
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
There is also an onModuleAttach hook which simply gets invoked, if your module is attached to a backend.
CheckHealth
You can implement
health checks
for your module, which can be called via REST; see more here.
GetSpecificRestResource
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 receive when you download the latest version of the native module SDK. The class will contain any attributes that you defined in the Dashboard. As mentioned above, these classes will be placed into 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.
@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={},
roleClassesMap={
"1=MyModule$RoleChecker"
,
"2=Basics$Role"
})
This generated class cannot be changed although changes to the data model can be done 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
;