Application
Applications are fundamental to understanding and building with KOS. This article aims to bridge the gap between the Applications concept article and the javadocs, covering best practices for application development while providing the context needed to understand how applications work and integrate within the KOS ecosystem.
Definition
What is a KOS application? There are two definitions there is the Application class which is the starting point of code execution. Then there’s the broader definition which is the KAB that includes the whole java project JAR. In this article references to the class with be denoted by the inline code formatting like so Application all other references to app, application, system app, refer to the entirety of the project.
Types of applications
There are two application classes in KOS, the base class Application and its subclass SystemApplication. Both classes are the starting point of code execution in the application. Where they differ is the project that they represent, the system app is the java project that defines the core functionality and performs all of the initial setup for a KOS device. Applications are additional modules loaded into KOS alongside the system app. All applications run within the single KOS process as separate modules rather than as independent processes. Applications provide additional functionality and integrations for the KOS device. For instance a device management agent would be an application, as it is not core to the functionality of a device. The app types also differ in KAB type and which sections they should be placed, see Kabs, Kab Types, and Sections
| System App | Application |
|---|---|
| Must be in the kos.system image section | Can be in any section. |
| KAB type must be kos.system | KAB type must be kos.app |
| Is always loaded and started first. | Start order not guaranteed |
appId in descriptor is always system | Needs a globally unique appId |
| Implements core functionality of device | Auxiliary functionality not specific to the device. |
| Extends SystemApplication | Extends Application |
Descriptor
Every application KAB needs a descriptor, to define which class is the application class so that KOS can start it and the appId which is used extensively in the KOS ecosystem. Descriptors can be used to include additional information that can be accessed from the Application. The kos key is reserved for internal use but additional fields can be added for custom use.
{
"kos": {
"app": {
"appClass": "kondra.device.mgmt.ManagmentApp",
"appId": "kondra.device-mgmt.agent"
}
},
"kondra": {
"field1": "valuableText",
"field2": 523
}
}Example `descriptor.json for an app
App accessing fields from descriptor
AppId
The appId is a key component to an application as it is used to partition off resources for the app. It’s used to provide a consistent location on disk to store data, as a prefix in endpoint paths, in message broker messages, and many other places to ensure that there is no overlap between applications. Since it is so prevalent in KOS, the appId should be carefully considered and remain static once set, as any changes to the appId has many knock on effects. For instance if the appId changes the endpoints will change and can break the UI if it was trying to reach the endpoints with the original appId. The appId should be short and globally unique, the recommended pattern for the appId is <organization name>.<project name>.<app name> for example kondra.innovation.customApp.
Starting apps
Applications can be loaded and started automatically by KOS, by placing the KAB in a manifest section with the prefix kos.autostart.*. KOS will never unload an application if it is autostarted. Dynamically loading and unloading apps can be done through use of the loadApp() method in AppService, the method returns an AppLoader object which can be used to start the app. For most use cases placing apps in an autostart section is sufficient.
Life cycle events
Application implements the Loadable interface which defines 5 lifecycle events load(), start(), started(), stop(), unload(). This gives standardization to the way that an app is started and stopped, specifying callbacks for setting up the app and callbacks to clean up resource usage when the app is stopped or unloaded. Here’s in short how these life cycles are used in Application for more detail refer to the javadoc.
| Callback | Description |
|---|---|
| load() | Initialization phase where resources are marshaled and beans are registered to the context. All beans added here will be autowired before start() is called. For SystemApplication, regions must be installed during this callback. |
| start() | Execution phase called after load() completes. The application begins its startup sequence and must complete all startup logic before returning. For SystemApplication, installAssembly() must be called during this callback. |
| started() | Post-startup notification indicating the application is fully operational. The app’s VFS (Virtual File System) and endpoints are now active and available for other applications to interact with. |
| stop() | Pre-shutdown callback triggered before the application is unloaded. At this point, the app’s endpoints and VFS have been disabled, preventing new interactions while allowing cleanup to begin. |
| unload() | Final cleanup phase where all application resources must be released and disposed of properly. This ensures the application can be fully unmounted and garbage collected without memory leaks. |
Inter application communication
In the Applications concept article it mentions that apps in KOS can be collaborative. This allows different stakeholders to create their own apps which will be responsible for their portion. This has the benefit of cleanly separating their work from the main development team. Take for example a payment terminal company creating an app to integrate with their payment terminal. The app could additionally have an SDK that the system app could use and build against. This way the system app is only concerned with its core functionality and can cleanly integrate with the payment terminal as necessary. Alternatively the manufacturer could provide integrations over the network, via endpoints and message broker topics. Here’s a bit about both of those options.
SDK’s
SDK’s allow applications to collaborate by directly calling methods in another application, making this the fastest way to interact with other applications. To use another app’s SDK is the same as any other maven dependency. The major difference between KOS SDKs and traditional Java SDKs is that the SDKs are generated from the app and strip out the implementation leaving only the method signatures.
KOS provides the annotations plugin for the annotations to generate an SDK, and the fastest and easiest way to create an app with an SDK is to generate it using the sdk-app KOS maven archetype see more here. When the project is properly setup the only requirement for creating the SDK is to annotate the classes in the app that are to be included in the SDK with the @Sdk annotation. This will automatically include all of the methods of the annotated class in the SDK. The annotations plugin has additional functionality manipulate the generated SDK. These manipulations include excluding methods from the SDK, or removing interfaces from a class.
Network
The alternative approach to an SDK for inter application collaboration is to create an API over the network. Apps can create endpoints using Controllers, these endpoints will be available once the app is started, and are discoverable through the API Browser tool available in KOS Studio. Other apps can reach endpoints by making use of the ApiClient. This class has many convenient methods to reach different endpoints, for instance there are methods to reach endpoints on other nodes. There are also methods to reach endpoints on the local node and the primary node. These methods make it convenient to collaborate with other applications even without an SDK.
Applications that depend on other applications at runtime may encounter errors attempting to call nonexistent methods or making failed network requests, applications should verify dependencies are present and running before attempting to use them. As of KOS 1.9.0 there are callbacks in Application to help write code that is reactive to the presence of other applications, before this the Application would have to implement the AppListener interface. The methods that the Application class now provides are whenAppStarted() , isAppInstalled(), and isAppAutostart(). All of the methods use the appId to determine which app to look for. The whenAppStarted() method takes in a lambda function that is executed when the app is fully started. isAppInstalled() returns true if the app is part of the currently running image, and isAppAutostart() returns true if the app is in an autostart section. These methods allow an app to react to the different ways that an image may be configured, and perform initialization when other apps come online.
