LAPIS is a framework for incorporating computational steering in Java and MATLAB applications. It allows applications to expose variables through a REST interface so that the values of those variables can be retrieved and set at runtime by users or external processes. It also allows programmers to build networks of computationally-steered applications, each of which can be steered programmatically or manually through the REST interface exposed by each network node. LAPIS is designed to make it simple for engineers and scientists who do not have significant experience in computer programming to easily deploy a communication network for High Performance Computing applications.
LAPIS is inspired by the dissertation of Harrison B. Smith at The Ohio State University and is developed with the support of the National Science Foundation (NSF).
This is a high-level overview of the features of the LAPIS framework. Each feature will be covered in more depth in the sections on using LAPIS in MATLAB and Java.
The core feature of the LAPIS framework is the ability to "publish" variables, which exposes the variables through LAPIS's REST interface. Within an application, published variables can then be used like regular variables (though they should not be re-assigned). The present values of published variables can be retrieved through LAPIS's REST interface, and the values of these variables can be set through the same interface. LAPIS will propagate the change to the application code in a way that is transparent to the application.
Applications can also publish variables as "read-only" within LAPIS. The values of these variables are still available to be retrieved, but cannot be set, through LAPIS's REST interface.
LAPIS allows programmers to easily build networks of computationally-steered applications without the need for sophisticated software packages or network communication libraries. This makes it simple to implement applications which programmatically steer each other. Nodes within a LAPIS network can get and set each other's published variables. LAPIS handles the details of inter-process communication, so that client code remains simple.
As mentioned previously, LAPIS exposes the values of published variables through a REST interface for easy debugging, control, and monitoring. The values of published variables can be retrieved. The values can also be set if the variables have not been published as "read-only" within LAPIS. In addition to getting and setting variable values, LAPIS also exposes variable meta-data and network information through its REST interface. Further detail on the REST interface will be provided in a separate section.
LAPIS allows programmers to build networks of computationally steered applications. Each application (also known as a simulation, or model) within the network can access other applications' published variables.
To create a LAPIS network, you must first create a LAPIS coordinator node. The coordinator node within a LAPIS network is the node that is responsible for maintaining a record of all of the nodes on the network. This record is stored within the LAPIS framework, so the client does not need to be concerned about it. After the coordinator node has been created, you can add multiple non-coordinator nodes to the network. An important note is that the coordinator node is only responsible for keeping network information. No application specific data is transmitted or received by the coordinator at any time during a communication.
There are three key details that client programmers must be aware of regarding coordinator and non-coordinator LAPIS nodes:
- Coordinator nodes and non-coordinator nodes are created using different constructors for the LAPIS API object (LapisAPI in MATLAB, LapisApi in Java). The details for LAPIS API object creation are described in the sections on using LAPIS in MATLAB and Java.
- The coordinator node must be running before non-coordinator nodes can join the network. If a non-coordinator node starts up before the coordinator for its network is running, an exception will be thrown when the non-coordinator attempts to connect to the coordinator and subsequently fails.
- Each LAPIS network has only one coordinator, but a single application can participate in multiple LAPIS networks - such as by instantiating multiple LAPIS API Objects that connect to different LAPIS networks.
LAPIS includes a MATLAB client for incorporating steering in MATLAB applications.
To use the LAPIS client in MATLAB, you will need the LAPISData.m and LapisAPI.m files, and the lapis-matlab .jar file (as of the time of this writing, lapis-matlab-1.0-SNAPSHOT-jar-with-dependencies.jar).
Use MATLAB's javaaddpath
command to add the lapis-matlab .jar file to the Java path within MATLAB. All necessary Java dependencies are packaged within this .jar file, so it will be the only .jar file you need in order to use LAPIS.
To start using LAPIS in your MATLAB application, you need to instantiate a LapisAPI
object.
myNodeName = 'MATLAB-node'
myAddress = 'http://127.0.0.1:7777'
% create coordinator LAPIS node
lapisApi = LapisAPI(myNodeName, myAddress)
Note that the LapisAPI constructor above creates a coordinator node. If you intend to use LAPIS in a standalone application, and not as part of a network of LAPIS nodes, you should use the constructor that creates a coordinator node.
The first example of instantiating the LapisAPI
object showed you how to set-up a coordinator node. To set up a non-coordinator node, use the constructor that takes three arguments:
myNodeName = 'non-coordinator-node'
coordinatorAddress = 'http://127.0.0.1:7777'
myAddress = 'http://127.0.0.1:8888'
lapisApi = LapisAPI(myNodeName, coordinatorAddress, myAddress)
Note that a non-coordinator node will immediately attempt to connect to the network coordinator at the specified coordinator address. An exception will be thrown if it is unable to connect.
To publish MATLAB variables in LAPIS, you must use LAPISData
wrapper objects. Using standard MATLAB datatypes are not directly supported by LAPIS due to MATLAB's variable reference methodology.
% instantiate a LAPISDAta object
anArray = LAPISData('anArray', [1 2 3 4 5])
LAPISData
objects have two fields. The first, .name
, is the name that LAPIS will use for the variable if it is published by the application. The value of the variable can be retrieved, by name, in the REST interface, and other nodes on a LAPIS network can get and set the variable using the published name and the name of the node on which the variable was published. In the code example above, the .name
field of the instantiated LAPISData
object is set to 'anArray'.
The second field of the LAPISData
object is the .data
field. This holds a reference to the current value of the variable. In the example code, the .data
field is initially assigned an array with five elements.
Use the LapisAPI instance you have already created to publish your LAPISData object:
anArray = LAPISData('anArray', [1 2 3 4 5])
% lapisApi is a previously-created LapisAPI object
lapisApi.publish(anArray)
The variable is now published, and can be retrieved and set through the REST interface and by other nodes on the network.
Currently, the MATLAB implementation of LAPIS supports scalars, and 1D, 2D, and 3D vectors. Other datatypes, such as cell arrays, structs, and objects are not currently supported.
In your MATLAB code, use the .data
field of the published LAPISData
object to reference the value of the variable. The value returned by .data
includes any changes that may have been made by the variable being set manually through LAPIS's REST interface or programmatically from another LAPIS node. When you set the value of .data
from within your application, the new value is available through the REST interface and to other network nodes.
anArray = LAPISData('anArray', [1 2 3 4 5])
lapisApi.publish(anArray)
% get the current value of the published variable
valueOfTheArray = anArray.data
% increment each element within the array
anArray.data = 1 + anArray.data
% change the value of the array
anArray.data = [-5 -4 -3 -2 -1]
Note that if the value of a published variable is assigned to another variable, and that second variable is subsequently modified, the value of the published variable does not change.
anArray = LAPISData('anArray', [1 2 3 4 5])
lapisApi.publish(anArray)
% this does NOT change the value of anArray.data
someValue = anArray.data
someValue = 1 + someValue
% someValue now [2 3 4 5 6]
% anArray.data is still [1 2 3 4 5]
% this DOES change the value of anArray.data
anArray.data = anArray.data + 1
% anArray.data is now [2 3 4 5 6]
Note that you should not re-assign the variable that refers to your published LAPISData
object if the intention is to still use the variable on a LAPIS network.
% do not do this -- re-assigning a LAPISData variable
anArray = LAPISData('anArray', [1 2 3 4 5])
anArray = [5 6 7 8]
Re-assigning a LAPISData
variable, as in the example above, causes the LAPISData object to be overwritten, resulting in the loss of reference to the LAPISData object. This will render the modification of the variable by the application programmer futile. In order to allow the LAPIS API to keep the reference to the LAPISData object and subsequently allow the network to maintain a current record of the LAPIS variable data, the reassignment of data should occur by reference the .data
property of the LAPISData object.
% do not do this -- re-assigning a LAPISData variable
anArray = LAPISData('anArray', [1 2 3 4 5])
anArray.data = [5 6 7 8]
To "un-publish" a published variable, use the redact
method:
% lapisApi is an instance of LapisAPI
% lapisData is an instance of LAPISData that has already been published
lapisApi.redact(lapisData)
It is rare that applications will need to un-publish variables, so use of the redact
method does not need to be used in all applications.
When you have multiple nodes on a LAPIS network, you can retrieve the value of variables that have been published by other nodes. If you have a variable called var1 that has been published by a node called node1, you would retrieve the variable value like this.
% retrieve the value of var1, a variable that was published by node1
valueOfVar1 = lapisApi.get('node1', 'var1')
Note that the value returned by the get
call is not a LAPISData
object. It is equal to the value returned by a call to the .data
field of the published LAPISData
object on node1. It is most likely an array or matrix.
If a node has published a variable and it has not been published as "read-only", then other LAPIS nodes on the same network can set the value of the variable.
% set the value of var1, a variable published by node1
valueToSet = magic(10)
lapisApi.set('node1', 'var1', valueToSet)
LAPIS includes a Java client for incorporating steering in Java applications.
To use the LAPIS client in Java, you will need the lapis-java-client .jar file (as of the time of this writing, lapis-java-client-1.0-SNAPSHOT-jar-with-dependencies.jar).
To start using LAPIS in your Java application, instantiate a edu.osu.lapis.LapisApi
object:
// this creates a coordinator node
String myNodeName = "Java-node";
String myAddress = "http://localhost:7788";
LapisApi lapisApi = new LapisApi(myNodeName, myAddress);
As of this time, variables published through LAPIS's Java client must be one-, two-, or three-dimensional arrays of a Java primitives.
final int[] ints = new int[] {8, 6, 7, -5, 3, 0, 9};
//lapisApi is an instance of edu.osu.lapis.LapisApi
lapisApi.publish("publishedInts", ints);
In the example above, an array of integers is published with the name "publishedInts". Other LAPIS nodes on a network can access the present state of ints
by using the published name of the variable.
Note that the recommend practice is to publish final
variables. Published variables should not be re-assigned. Re-assigning a published variable causes two problems. It prevents the application from changing the value of the published variable that is exposed through the REST interface, and it prevents any changes made through the REST interface (either manually or programmatically by other nodes in a LAPIS network) from being seen within the Java application.
Most applications will have no need to un-publish variables, but if you wish to remove a previously-published variable, you can use the redact
method:
//lapisApi is an instance of edu.osu.lapis.LapisApi
lapisApi.redact("nameOfPreviouslyPublishedVariable");
Note that the argument passed to the redact
method is the name of the published variable within LAPIS, not the published object itself.
Applications within the same LAPIS network can get and set each other's published variables, as in the example below. Note that the application must refer to other nodes' published variables using the "full name" of the published variable, i.e. published name + @
+ node name. Note: This WILL change in a future release.
// "doubles" is an array of doubles published by a node named "otherNode"
String variableName = "doubles";
String otherNodeName = "otherNode";
String variableFullName = variableName + '@' + otherNodeName;
// get the value of the published variable by referring to the "full name"
double[] localDoubles = lapisApi.getArrayOfDouble(variableFullName);
// make some change and set the value on the other node
for(int i = 0; i < localDoubles.length; ++i) {
localDoubles[i] = Math.sqrt(localDoubles[i]);
}
lapisApi.set(variableFullName, localDoubles);
This release includes some changes and new features, discussed below. Note that examples provided are for the MATLAB client. For the Java client, please look at the source code, unit tests, and Javadoc for the the lapis-java-client project, and at the examples provided earlier in this document.
The function for publishing variables has changed so that you no longer need to specify a name string when calling the publish
function. Internally, LAPIS will use the .name
field of the LAPISData
passed to the function as the published named of the variable.
Here is an example:
finishFlag = LAPISData('finishFlag', [0]);
lapisApi.publish(finishFlag);
% the name of the published variable will be 'finishFlag'
For reference, this is how variables were published prior to this release:
% this is code for the previous approach -- do not do this anymore
finishFlag = LAPISData('finishFlag', [0]);
lapisApi.publish('finishFlag', finishFlag);
It is now possible to build networks of LAPIS nodes across independent nodes on an IP network. When constructing your LapisAPI
object in MATLAB, you will have to specify the externally visible address of your LAPIS node--that is, an address visible to other servers on your network (but not necessarily to the wider internet). So, whereas you might have used 'http://127.0.0.1:7777'
as the address of your LAPIS node before, now you'll want to use the address by which other servers can access your node, such as 'http://192.168.1.2:7777'
. For more information, please refer to literature regarding IP networking.
The following examples demonstrate the use of addresses that will be visible to other servers on the network:
% constructor to create coordinator node
myAddress = 'http://192.168.2.2:7777'; % note: myAddress is also the coordinator address in this example
nodeName = 'Node1';
lapisApi = LapisAPI(nodeName, myAddress);
% constructor to create non-coordinator node
myAddress = 'http://192.168.2.55:7777';
coordinatorAddress = 'http://192.168.2.2:7777';
nodeName = 'Node2';
lapisApi = LapisAPI(nodeName, coordinatorAddress, myAddress);
Note that the http://192.168.xxx.xxx
address is a typical address assigned by a DHCP server running on a standard home-networking router such as a Linksys (Cisco), or Belkin. Your subnet can be verified by checking the IP address of a computer that is connected to the same network on which your LAPIS network is intended to be configured.
To facilitate coordination among multiple nodes in a LAPIS network, LAPIS now allows applications to pause while waiting for other nodes to join a network, perform any initialization processing, and then declare themselves ready.
To wait for another node to declare that it is ready, use the waitForReadyNode
or waitForReadyNodeWithTimeout
function, as in the example below:
% waits 10000 milliseconds--or 10 seconds--for 'Node1' to become 'ready'
lapisApi.waitForReadyNodeWithTimeout('Node1', 10000);
% waits indefinitely for 'Node2' to become 'ready'
lapisApi.waitForReadyNode('Node2');
A node does not have to already be present on the LAPIS network for another node to begin waiting for it. The waitForReadyNodeWithTimeout
function blocks until the specified node has become ready or the timeout is reached. If the timeout is reached, an exception will be thrown, so catch the exception if you wish to continue processing in the event of a timeout. The waitForReadyNode
function blocks indefinitely. It will not return until the specified node has joined the network and declared itself to be ready. Use of waitForReadyNodeWithTimeout
should be preferred over use of waitForReadyNode
.
Nodes upon which other nodes are waiting will need to declare themselves ready using the ready
function. This 'ready' state is visible to all other nodes on the same LAPIS network. Any node currently waiting on the node that becomes ready will continue processing (the waitForReadyNode
or waitForReadyNodeWithTimeout
function will return), and any node that subsequently attempts to wait on the 'ready' node will continue processing almost immediately.
Nodes which must no longer appear as 'ready' to the rest of the network should call the notReady
function. Note that calling the notReady
function when ready
has not been called will have no effect. Similarly, if a node is already in the 'ready' state, further calls to ready
have no effect.
Applications do not have to use the functionality that LAPIS provides for waiting on nodes and declaring nodes 'ready', but these features help to build networks of applications that work together.
Before clearing your LAPIS network variable in MATLAB by using the clear
command, you must use the shutdown
method in order to force MATLAB to clean up the network. If this is not done, the instantiated LAPIS network reference will continue to exist and will not allow another network to be created on the same port.
To shutdown a LAPIS network, simply type
%shuts down the network
lapisApi.shutdown()
%continue with other clearing functionality, such as 'clear' ...
clear all;
close all;
clc;
The client-side communication code has been re-implemented using Apache HttpClient. This may require MATLAB users to make a configuration change in order to run LAPIS, depending on their current configuration. The following line from $matlabroot/toolbox/local/classpath.txt MUST be commented out (if it is present) or LAPIS will not work:
$matlabroot/java/jarext/axis2/httpcore.jar
To edit $matlabroot/toolbox/local/classpath.txt, do the following:
- Select a text editor, right-click, and select 'Run as Administrator'.
- In MATLAB, type the following to display the location of the classpath.txt file:
[matlabroot '\toolbox\local\classpath.txt']
- Open the classpath.txt file in your editor from the first step.
- Comment out the line that ends in 'httpcore.jar'
- Save the file and exit the text editor.
- Re-start MATLAB.
In the Java client in LAPIS 0.4, separate arguments are passed for the node name and variable name in get and set calls:
// get the "bytes" variable published by node "non-coor"
byte[] retrieved = coordinatorLapis.getArrayOfByte("non-coor", "bytes");
//set the value of variable "bools" published on node "non-coor"
boolean[] different = // create array...
coordinatorLapis.set("non-coor", "bools", different);
Since LAPIS is designed to be easily accessible to the inexperienced programmer, accessing a LAPIS network using standard tools - such as a web browser - is included as part of the implementation.
The LAPIS network is exposed through a RESTful API making it easily accessible for testing and verification.
For GET
HTTP operations (which are the only operations shown below in the examples) it should be noted that you only need a web browser on a computer that has access to the same IP network to which your LAPIS network resides.
For more sophisticated HTTP operations (such as PUT
, POST
, and DELETE
, many REST clients exist (desktop, web, and browser based). The REST client that will be used as an example here can be downloaded as a Google Chrome extension. Note that you will need to run Google Chrome in order to download and use. Please view the help of the extension for information on how to use it.
To view your network coordinator, either open up Advanced REST Client in Google Chrome or any web browser. For the next examples, we will assume that the LAPIS network coordinator you are debugging is located at 'http://localhost:7777'
, and that there are two nodes on the network located at 'http://localhost:7777'
and 'http://localhost:8888'
.
To test whether or not your coordinator is running, enter http://localhost:7777/coordinator
in the URL bar and perform a GET
operation (for a browser, just execute the URL address). If the coordinator is running, the server will respond with a 200
HTTP code and the following JSON
[
{
"nodeName": "Node2",
"url": "http://localhost:8888"
},
{
"nodeName": "Node1",
"url": "http://localhost:7777"
}
]
To test for the existence of a node on a network, enter http://localhost:8888/network
in the URL bar and perform a GET
operation. A JSON list will be returned such as:
{
nodeName: "Node2"
url: "http://localhost:8888"
}
Let's assume that the node at http://localhost:7777
has published a variable x
that holds a 5 element vector and we need to check it's value on the LAPIS network. To do this, enter http://localhost:7777/model/x
in the URL test field and perform a GET
operation. If the variable exists, you should get the response:
{
"name": "x",
"originalType": "[D",
"data": [
1.0,
2.0,
3.0,
4.0,
5.0
]
}
To view all published variables of the node at 'http://localhost:8888'
, simply enter http://localhost:8888/metadata
in the URL bar. If the node exists, it will return a JSON string containing information about all the published variables:
[
{
"name": "node1finish",
"lapisPermission": "READ_WRITE"
},
{
"name": "finishFlag",
"lapisPermission": "READ_WRITE"
},
{
"name": "simFinishFlag",
"lapisPermission": "READ_WRITE"
},
{
"name": "node2copy",
"lapisPermission": "READ_WRITE",
"type": "one dimensional array of double"
},
{
"name": "ready",
"lapisPermission": "READ_WRITE"
}
]
We have only shown GET
operations here, as other HTTP operations are more involved and do not normally need to be used in the REST client debugging. If more sophisticated HTTP operations are required, please consult the Java Docs and Wiki for the full REST resource dictionary.
Logging can now be configured using the log4j.properties
file that is included with the other MATLAB files for this release. In MATLAB, LAPIS locates this file in the current directory and uses it to configure logging whenever a LapisAPI
object is instantiated. Instructions on the use of log4j can be found at http://logging.apache.org/log4j/1.2/manual.html, though most users will not have to make any changes to the log4j configuration.