Runtime Environment

Principle

A Runtime Environment is a module nature for defining the set of APIs available to an Application developer on a Kernel. It is built by aggregating a set of Kernel APIs.

Depending on the SDK that you are using, refer to the SDK 6 module nature page or to the SDK 5 module nature page for more information about Runtime Environment module nature.

Building a Runtime Environment is one of the 2 solutions to define the APIs of a Kernel, as described in the section Expose APIs. Having the set of APIs named and versioned in a Runtime Environment allows to maintain, share and document it outside of a specific Kernel implementation.

Once built, a Runtime Environment module contains the following elements:

  • A JAR file with the whole library of APIs (.class files and .java), used by Application projects to compile Application code;

  • A JAR file with the kernel.api file defined in the module (if any), used by Kernel projects to fetch all the kernel.api files (by transitivity) to expose APIs when building the Firmware and the Virtual Device;

  • A JAR file with the Javadoc of the APIs for documentation.

The following figure shows the overall build flow:

../_images/runtime_environment_build_flow.png

Create a new Runtime Environment Module

A Runtime Environment Gradle project is created with the com.microej.gradle.runtime-environment plugin.

plugins {
   id("com.microej.gradle.runtime-environment") version "1.2.0"
}
Copy to clipboard

Kernel APIs as Dependencies

The Kernel APIs can be declared as dependencies of the module. For example, the following dependencies declare a Runtime Environment that aggregates all classes, methods and fields defined by EDC, KF, BON, MicroUI and BasicTool Kernel APIs modules.

dependencies {
   implementation("com.microej.kernelapi:edc:1.2.0")
   api("ej.api:edc:1.3.4")
   implementation("com.microej.kernelapi:kf:2.1.0")
   api("ej.api:kf:1.5.1")
   implementation("com.microej.kernelapi:bon:1.4.0")
   api("ej.api:bon:1.4.0")
   implementation("com.microej.kernelapi:microui:3.6.0")
   api("ej.api:microui:3.6.0")
   implementation("com.microej.kernelapi:basictool:1.4.0")
   api("ej.library.runtime:basictool:1.7.0")
}
Copy to clipboard

Warning

  • Unlike SDK 5 (MMM), Kernel API dependencies are not transitively fetched with SDK 6. Therefore, they must be explicitly added.

  • The Libraries dependencies must be declared with api (as shown in the example above) to be consumable by the Kernel.

  • Versions of EDC higher than 1.3.5 are not compatible with the creation of a Runtime Environment. To make sure to use version 1.3.5 of EDC, use this snippet in your build.gradle.kts file:

    configurations {
       "runtimeClasspath" {
          resolutionStrategy.force("ej.api:edc:1.3.5")
       }
    }
    
    Copy to clipboard

Kernel APIs as Project File

The Kernel APIs can also be defined in a file in the Runtime Environment directly. The file must be named kernel.api and stored in the src/main/resources folder.

Add Add-On Processors

Note

This feature is available for SDK 5 only.

When the Runtime Environment includes an Add-On Library which uses an Add-On Processor, this Add-On Processor must be declared as a direct dependency in the Runtime Environment.

The Add-On Processor dependency line can be retrieved as follows:

  • In your target module repository, go to the Add-On Library folder,

  • Open the ivy-[version].xml file,

  • Search for the dependency line with conf="addon-processor->addon-processor"

    <ivy-module xmlns:ea="http://www.easyant.org" xmlns:ej="https://developer.microej.com" xmlns:m="http://ant.apache.org/ivy/maven" version="2.0" ej:version="2.0.0">
       <info organisation="com.mycompany" module="mylibrary" revision="M.m.p" status="release" publication="20220523165033">
          ...
       </info>
       <configurations>
          ...
          <conf name="addon-processor" visibility="public" description="Addon processors dependencies."/>
       </configurations>
       <publications>
          ...
       </publications>
       <dependencies>
          <dependency org="ej.api" name="edc" rev="1.3.3" conf="default->default;provided->provided"/>
          ...
          <dependency org="com.mycompany.addon" name="mylibrary-processor" rev="x.y.z" conf="addon-processor->addon-processor"/>
          ...
       </dependencies>
    </ivy-module>
    
    Copy to clipboard
  • In the Runtime Environment module description file, declare the addon-processor configuration in the list of configurations

    <conf name="addon-processor" visibility="public" description="Add-On Processors dependencies."/>
    
    Copy to clipboard
  • Paste the Add-On Processor dependency line

Warning

If the Add-On library version is changed, the Add-On Processor version must be updated.

Here is a list of known libraries using an Add-On Processor:

  • NLS:

    <dependency org="com.microej.tool.addon.runtime" name="binary-nls-processor" rev="<version>" conf="addon-processor->addon-processor"/>
    
    Copy to clipboard
  • Wadapps:

    <dependency org="ej.tool.addon.wadapps" name="wadapps-processor" rev="<version>" conf="addon-processor->addon-processor"/>
    
    Copy to clipboard
  • JavaScript:

    <dependency org="com.microej.tool.addon.runtime" name="js-processor" rev="<version>" conf="addon-processor->addon-processor"/>
    
    Copy to clipboard

Use a Runtime Environment in an Application

The Runtime Environment dependency must be declared in the Application project as following:

For an Application:

microejRuntimeEnvironment("com.mycompany:myruntime-environment:1.0.0")
Copy to clipboard

For a Kernel Application:

implementation("com.mycompany:myruntime-environment:1.0.0")
Copy to clipboard

Note

If you want to add an other library dependency, make sure it is has been built on this Runtime Environment. Otherwise this could lead to inconsistent situations, for example by using an API not available at runtime. An other approach is to add it to the Runtime Environment.

Extend a Runtime Environment

Note

This feature is available for SDK 5 only.

In a Kernel, Foundation and Add-On libraries can be extended by adding new methods to their existing classes. For example, it allows to add new methods to the class java.lang.String of the module ej.api#edc. This is done thanks to the Class Extender tool. This tool works at binary level and is able to inject methods from one class to another. Extensions can thus be independently compiled and be retrieved by the Kernel and applied during a Multi-Sandbox Executable build.

To make the extensions available to Application developers, the Runtime Environment has to be extended too.

The following diagram illustrates the process of extending the default java.lang.String class from [EDC] from a Kernel developer point of view:

../_images/graph_build_string_methods.png

The extension must be applied in 2 locations:

  1. In the Runtime Environment. This ensures that Applications developers can see and use the new methods. The custom Runtime Environment must contain the following element:

    • the API to extend, as a dependency. Here this is the EDC Foundation Library API, which contains the java.lang.String class we want to extend. We can add it transitively through its kernelapi:

      <dependency org="com.microej.kernelapi" name="edc" rev="1.0.6"/>
      
      Copy to clipboard
    • a Kernel API file definition in the src/main/resources folder which includes the new methods. For example:

      <?xml version="1.0" encoding="UTF-8"?>
      <require>
         <method name="java.lang.String.myNewMethod(int)java.lang.String"/>
         <method name="java.lang.String.myOtherNewMethod()void"/>
      </require>
      
      Copy to clipboard
    • the new version of the Java source of the API to extend. This class overrides the original class fetched from the dependency. Therefore it must include all the methods, the ones existing in the original class as well as the new methods, with their Javadoc specification. In our example, we must add a new String.java source file in the src/main/java/java/lang folder, and add the new methods:

      public String myNewMethod(int number) {
         return "My number is " + number;
       }
      
      public void myOtherNewMethod() {
         System.out.println("Hello!");
      }
      
      Copy to clipboard

      This class overrides the java.lang.String class fetched from the EDC dependency.

    Once built, the custom Runtime Environment contains the new methods and can be used in the Applications projects.

  2. In the Kernel. The EDC implementation is extended during the Kernel build thanks to the Class Extender tool. Refer to the Class Extender tool README and especially to the chapter Include Class Extender During Firmware Project Build to learn how to integrate it in a Kernel build.

    MicroEJ Corp. provides some ready-to-use extension modules: