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.0.0"
}

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")
}

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")
       }
    }
    

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>
    
  • 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."/>
    
  • 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"/>
    
  • Wadapps:

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

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

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")

For a Kernel Application:

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

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"/>
      
    • 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>
      
    • 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!");
      }
      

      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: