-
Notifications
You must be signed in to change notification settings - Fork 80
Java bridge
Anode includes a bridge to a java runtime, so that modules can be written (and, ultimately, dynamically deployed) as Java libraries. Although the principal target of this feature is to integrate with the Android platform, the mechanism works with any Java Runtime Environment (JRE) and is therefore able, in principle, to integrate with a JRE on the desktop or with a server J2EE environment.
These notes aim to provide the information needed to implement modules in Java, or a combination of JavaScript and Java, and are not intended to be a tutorial on the internal design of the bridge, although implementation details are mentioned where this informs the discussion.
Wherever possible, terminology is adopted from WebIDL. Terms defined in WebIDL are used here without repeating their definition.
A detailed familiarity with WebIDL and its Java binding will be very useful in understanding these notes, and implementing Java modules in general.
Specifically, note that the concept of module is no longer part of WebIDL. In these notes, the term module is used to refer to a node.js module, and has no formal connection with WebIDL. However, we will more-or-less-interchangeably use the term module to refer to:
-
a collection of WebIDL interfaces that, together, define a coherent unit of functionality;
-
an implementation of that functionality as a Java library;
-
an implementation of that functionality that is loadable as a node.js module.
The bridge is still under development. This means that the features described in these notes may be unimplemented, or not stable, and are also liable to change.
Note also that WebIDL itself is still in a state of flux. We are trying to ensure that we are aligned with the latest editor's draft, but expect differences to emerge from time to time while the bridge implementation catches up with specification changes.
The Java bridge allows a module's functionality to be implemented as a Java library. The functionality of the module is exposed to the bridge by implementing the IModule
interface, defined as follows:
package org.meshpoint.anode.module;
public interface IModule {
public Object startModule(IModuleContext ctx);
public void stopModule();
}
where IModuleContext
is a context interface that exposes the module's exports (ie the global object of the context created for the module) and which may be subclassed on individual platform implementations to provide platform-specific context or state required by a module. The IModuleContext
interface is declared as follows:
package org.meshpoint.anode.module;
public interface IModuleContext {
public Object getModuleExports();
public long getEventThreadId();
}
The Java bridge is itself a module, with the name bridge
; this exposes a load()
method that triggers the loads a Java module (specified by package and class name). Thus, typically, there will be a top-level JavaScript file that encapsulates the creation of the module context and loading of the Java-side implementation. For example, suppose a contacts
module is implemented by the com.example.ContactsModule
class, there would be a contacts.js
deployed as a module as follows:
/* contacts.js : load and export the java contact module */
var bridge = require('bridge');
return(bridge.load('com.example.ContactsModule', this));
Note how the module's global object (or "exports") is passed in to the load()
call; this in turn is passed into the Java module implementation via the module context object.
The bridge is based on the idea that the Java modules will implement an API that has been specified in WebIDL and, broadly, this means that the API consists of a series of WebIDL interfaces, each with a number of declared attributes and operations. Entities in these attribute and operation declarations are typed with a WebIDL type, which may be one of the predefined types, or may be an interface or dictionary type defined in the IDL for the module.
It is usual for interface definitions in WebIDL to be strongly statically typed, even though JavaScript itself is loosely and dynamically typed. Thus, the Java bridge is primarily designed to support the implementation of interfaces in this declaratively typed manner.
However, WebIDL does support the any type, and there are instances where it is not possible, or not convenient, to indicate a more specific type in the declaration of an entity. The bridge therefore does also provide a dynamically typed API, is a way of accessing JavaScript objects, determining their type dynamically, and introspecting their properties and methods, without access to any static type declaration.
A separate section provides further details of the bridge type mapping.
The bridge is implemented so that Java is entered directly by the main JavaScript thread whenever there is a method invocation (or, more accurately, a call of an operation) on an object implemented in Java.
Such invocations must not block, so that the JavaScript thread is not blocked. Thus, the operation must either complete synchronously without blocking, or must return immediately but then complete asynchronously. It is typical for long-running operations (eg operations that depend on IO, or compute-intensive operations such as database searches) to be specified as async operations whose completion is signalled by sending an event or by calling a callback.
In a Java module such operations must be handed-off to a separate thread, allowing the main thread to return. Once the operation is complete, the async thread may then call the callback, and may then exit, or be returned to a pool of idle threads waiting for more work.
All anode instances run in a single process, and therefore all java-side operations are also performed in a single VM. Each node instance - or isolate - will have a main thread (which will always be the thread that calls startModule()
when a module is loaded for that isolate). However, other operations in the VM may be performed in threads that are shared between isolates. One example of this is finalization - all objects are finalized in the VM's finalizer thread irrespective of which isolate they belong to - but individual modules may also instigate worker threads that are shared between isolates.
Therefore, you need to take care to synchronise appropriately in the module implementation, and know which isolate an object is associated with, since it cannot always be determined just be knowing the current thread. The IModuleContext.getEventThreadId()
may be used to identify the main JavaScript thread associated with any given isolate.
The anode repository contains an SDK containing the classes needed for development of Java modules.
See the bridge tutorial for an example of implementation of a Java module.