Embedding BPjs in an Application¶
Overview¶
The BPjs library can be embedded in larger Java programs. The setting is useful in cases where you want the control logic to be implemented using behavioral programming, and thus need to translate incoming signals to BP events, and selected BP events to instructions to, e.g. actuators, databases, or web-services.
The layers of an application running a BPjs BProgram
are described in the figure below.
The BP code (top layer) is the BPjs code, written in Javascript. It can interact with
the BPjs runtime using the The bp Object. The host application can make
other Java objects available to the Javascript code, as will be explained later.
The BPjs layer serves as a runtime environment for the BProgram. The host application has
to instantiate a BProgram
object, and pass it to a BProgramRunner
. The host application
can listen to events of the running b-program (such as start, end, b-thread added, and - of course - b-event selected).
Additionally, the host application can provide custom event selection strategy, in case
the default one it not good enough.
Note
Why is ``BProgram`` separated from ``BProgramRunner``?
Because a b-program is also
a model that can be verified rather than ran. The same BProgram
object can be passed to a verifier object for verification.
Steps for B-Program Embedding¶
Code setup¶
Add BPjs to your classpath.
See Obtaining BPjs.
Decide which BProgram
subclass you need.
BProgram is an abstract class. Its concrete sub-classes differ on how they obtain their source code. ResourceBProgram reads the code from a resource included with the code (typically, a .js file bundled in the project’s .jars). StringBProgram, on the other hand, takes a Java String as source. Of course,BProgram
can be directly extended as needed.
Write the BPjs code.
The code will interact with the runtime using thebp
object. Additional Java classes can be made available to it by using Rhino’s import directives, or by adding Java objects to the program’s scope (see below).
At Runtime¶
- Instantiate the proper
BProgram
sub-class, and supply it with the source BPjs code. - If needed, set a new
EventSelectionStrategy
. When no strategy is supplied, SimpleEventSelectionStrategy will by used. This strategy randomly selects an event from the set of events that are requested and not blocked. - If needed, add Java objects to the global b-program scope using putInGlobalScope.
- If the host Java program will push external events to the b-program, make the b-program wait for these events by calling
bprog.setWaitForExternalEvents(true)
. - Instantiate a
BProgramRunner
object, and supply it with theBProgram
instance. - Add listeners to the runner.
- In the common case when the program needs to wait for external events (such as GUI interactions), set the
isWaitForExternalEvents
property of theBProgram
totrue
. - Call
BProgramRunner::start()
.
The BProgram will start running. Life-cycle and behavioral events will be passed to the listener objects. In case the host application would like to push an external event to the embedded b-program (e.g. because of a network request, or a user click), it should use the BProgram
’s enqueueExternalEvent method.
Tip
BPjs’ source code contains many examples of embedded BPjs programs - most of the unit tests that involve a b-program. For a more complete example, refer to the RunFile class, which implements the command-line tool for running BPjs code.
Tip
SampleBPjsProject can serve as a template project for embedding BPjs in a Java application. You can fork it on GitHub and start building your application from there.
External Events vs. Internal Events¶
There is no difference between external and internal events – both are instances of the Java BEvent
class, or a subclass of it. However, there may be a difference in how these events are treated by the event selection strategy. External events are made available to the strategy using an “external event queue”. An event selection strategy may choose to ignore this queue whenever it can choose an event requested by a b-thread. But a strategy can also decide to choose an event from the queue even when there are internal events that are requested and not blocked.
All event selection strategies currently included with BPjs ignore external events when there are internal ones available for selection. This choice makes the system easier to reason about, as it gets to complete its reaction to one external event before it starts reacting to a new one. But this does not have to be the case.
Redirecting BPjs’ Log¶
By default, BPjs’ log (used by b-programs through, e.g., bp.log.info("hi!")
) is written to standard out. It is possible to redirect this log into other streams, by calling:
BProgram#setLoggerOutputStreamer(PrintStream newOut)
Changing BPjs’ Executor Services¶
BPjs uses Java’s executor services for b-program execution and verification. Normally, BPjs uses a fixed thread pool, with one thread per CPU. Client code can change this, by passing a customized ExecutionServiceMaker
instance to BPjs#setExecutorServiceMaker
.
This type of customization is useful when, for example, an application wants to use BPjs in a single-threaded manner, or when an application needs to preserve CPUs for other tasks.