Multi-Sandbox Enabled Libraries
A Multi-Sandbox enabled library is a Foundation Library or an Add-On Library that can be embedded by a Kernel with its APIs exposed to Features.
A library requires specific code for enabling Multi-Sandbox in the following cases:
it implements an internal global state: lazy initialization of a singleton, registry of callbacks, internal cache, …,
it provides access to native resources that must be controlled using a Security Manager.
Otherwise, the library is called a stateless library. A stateless library is Multi-Sandbox enabled by default: it can be embedded by the Kernel, and its APIs are directly exposed to Features without code modification.
Note
This chapter generally applies to any Kernel code, not just libraries.
Manage Internal Global State
A library may define code that performs modifications of its internal state, for example:
lazy initialization of a singleton,
registering/un-registering a callback,
maintaining an internal global cache, …
By default, calling one of these APIs from a Feature context will throw one of the following errors:
java.lang.IllegalAccessError: KF:E=S1
at <Kernel Method>
...
at <Feature Method>
java.lang.IllegalAccessError: KF:E=F1
at <Kernel Method>
...
at <Feature Method>
The reason is that the Core Engine rejects assigning a Feature object in a static field or an instance field owned by the Kernel. See the KF library access error codes for more details. This prevents unwanted object links from the Kernel to the Feature, which would lead to stale references when stopping the Feature.
The library code must be adapted to implement the desired behavior when the code is called from a Feature context. The following sections describe the most common strategies applied on a concrete example:
declaring a static field local to the Feature,
allowing a field assignment in Kernel mode,
using existing Multi-Sandbox enabled data structures.
Declare a Static Field Local to the Feature
The Kernel & Features Specification (KF) defines Context Local Storage for static fields. This implies that the Core Engine allocates a dedicated memory slot to store the static field for each execution context (the Kernel and each Feature).
Context Local Storage for static fields is typically used when the library defines a lazy initialized singleton. A lazy initialized singleton is a singleton that is only allocated the first time it is required. This is how is implemented the well-known Math.random() method:
public class Math{
private static Random RandomGenerator;
public static double random() {
if(RandomGenerator == null) {
RandomGenerator = new Random();
}
return RandomGenerator.nextDouble();
}
}
To enable this code for Multi-Sandbox, you can simply declare the static field local to the context.
For that, create a kernel.intern
file at the root of the library or Kernel classpath (e.g., in the src/main/resources
directory) with the following content:
<kernel>
<contextLocalStorage name="java.lang.Math.RandomGenerator"/>
</kernel>
When the method is called in a new context, the static field is read to null
, and then a new object will be allocated and assigned to the local static field.
Thus, each context will create its own instance of the Random
singleton on demand.
Note
By default, reading a static field for the first time in a new context returns null
.
However, it is possible to write dedicated code to initialize the static field before its first read access.
See section §4.3.3 Context Local Static Field References of the Kernel & Features Specification (KF) for more details.
Allow a Field Assignment in Kernel Mode
It is possible to assign a Feature object in a static field or an instance field owned by the Kernel only if the Kernel owns the current context.
Such an assignment must be removed before stopping the Feature.
The common way is to register a FeatureStateListener at Kernel boot. This gives a hook to remove Kernel links to Feature objects when a Feature moves to the STOPPED
state.
Kernel.addFeatureStateListener(new FeatureStateListener() {
@Override
public synchronized void stateChanged(Feature feature, State previousState) {
if (feature.getState() == State.STOPPED) {
// Here, remove Kernel->Feature references
}
}
};
Without this, the Feature will remain in the STOPPED
state. Therefore, it will not be possible to uninstall it or start it again until the link is removed.
The remaining Feature objects referenced by the Kernel are called Kernel stale references.
Note
To help debug your Kernel, Kernel stale references are displayed by the Core Engine dump.
Use Existing Multi-Sandbox Enabled Data Structures
MicroEJ Corp. provides ready-to-use classes on the shelf that are Multi-Sandbox enabled. Among them, we can cite the following:
KernelObservable
: Implementation of Observable that can handle observers from any Module.KFList
: Implementation of a Collection with multi-context support.SharedPropertyRegistry
: Map of key/value properties.SharedServiceRegistry
: Map of API/implementation services.
Please contact our support team for more details on usage.
Implement a Security Manager Permission Check
A Multi-Sandbox enabled Foundation Library should protect Feature from accessing native resources. This is done by requesting a check to the current SecurityManager defined by the Kernel.
The following code is the typical code that must be written at the beginning of API methods.
void myAPIThatOpensAccessToANativeResource(){
if (Constants.getBoolean("com.microej.library.edc.securitymanager.enabled")) {
// Here, the Security Manager support is enabled.
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
// Here, the Kernel has registered a Security Manager
// Create a Permission with relevant parameters for the Security Manager to render the permission
MyResourcePermission p = new MyResourcePermission();
// Request the permission check.
// If the Kernel rejects the permission, it will throw a SecurityException
securityManager.checkPermission(p);
}
}
// Implementation code
// ...
}
Note
The code is wrapped by a static check of the Option(checkbox): Enable SecurityManager checks. By default, this option is disabled, so the SOAR automatically removes the code. This allows you to use your library in a Mono-Sandbox environment where ROM footprint matters. Your Kernel shall enable this option to trigger the Security Manager checks. See Implement a Security Policy for more details.
Known Foundation Libraries Behavior
This section details the Multi-Sandbox semantic that has been added to
Foundation Libraries in order to be Multi-Sandbox enabled.
Most of the Foundation Libraries provided by MicroEJ Corp. are Multi-Sandbox enabled
unless the library documentation (e.g., README.md
) mentions specific limitations.
MicroUI
Note
This chapter describes the current MicroUI version 3
, provided by UI Pack version 13.0.0
or higher.
If you are using the former MicroUI version 2
(provided by MicroEJ UI Pack version up to 12.1.x
),
please refer to this MicroEJ Documentation Archive.
Physical Display Ownership
The physical display is owned by only one context at a time (the Kernel or one Feature). The following cases may trigger a physical display owner switch:
during a call to Display.requestShow(Displayable), Display.requestHide(Displayable), Display.requestRender() or Display.requestFlush(): after the successful permission check, it is assigned to the context owner.
during a call to MicroUI.callSerially(Runnable): after the successful permission check it is assigned to owner of the
Runnable
instance.
The physical display switch performs the following actions:
If a Displayable instance is currently shown on the
Display
, the method Displayable.onHidden() is called,All pending events (input events, display flushes, call serially runnable instances) are removed from the display event serializer,
System Event Generators handlers are reset to their default EventHandler instance,
The pending event created by calling Display.callOnFlushCompleted(Runnable) is removed and will be never added to the display event serializer.
Warning
The display switch is performed immediately when the current thread is the MicroUI thread itself (during a MicroUI event such as a MicroUI.callSerially(Runnable)). The caller looses the display and its next requests during same MicroUI event will throw a new display switch. Caller should call future display owner’s code (which will ask a display switch) in a dedicated MicroUI.callSerially(Runnable) event.
The call to Display.callOnFlushCompleted(Runnable) has no effect when the display is not assigned to the context owner.
Automatically Reclaimed Resources
Instances of ResourceImage and Font are automatically reclaimed when a Feature is stopped.
BON
Kernel Timer
A Kernel Timer instance can handle TimerTask instances owned by the Kernel or any Features.
It should not be created in clinit code, otherwise you may have to manually declare explicit clinit dependencies.
Automatically Reclaimed Resources
TimerTask instances are automatically canceled when a Feature is stopped.
ECOM
The ej.ecom.DeviceManager registry allows to share devices across Features. Instances of ej.ecom.Device that are registered with a Shared Interface type are made accessible through a Proxy to all other Features that embed the same Shared Interface (or an upper one of the hierarchy).
ECOM-COMM
Instances of ej.ecom.io.CommConnection are automatically reclaimed when a Feature is stopped.
FS
Instances of java.io.FileInputStream, java.io.FileOutputStream are automatically reclaimed when a Feature is stopped.
NET
Instances of java.net.Socket, java.net.ServerSocket, java.net.DatagramSocket are automatically reclaimed when a Feature is stopped.
SSL
Instances of javax.net.ssl.SSLSocket are automatically reclaimed when a Feature is stopped.