Adding a Service to JICOS

Adding an external service to Jicos requires implementing the native code necessary to start, shutdown, and access the service, interfacing that code with Java, and moving data into and out of the service.


Topics

 

Implementing Access to Matlab

[ TOP ]

The first decision to make is what functionality is necessary to expose to the Task. This is, and probably should be, different than all the functionality created by the JNI. Any functionality that could alter the normal operation of the service (startup, shutdown, ...) should not be available in the ProxyServiceExternal.

In the case of Matlab, there really is only one function

 

Starting and Stopping Matlab

[ TOP ]

 

Putting information into Matlab

[ TOP ]

One of the major challenges of moving data from Java to C is data storage. More specifically, how the two languages store the values in memory. For example, two-dimensional arrays. In Java, they are an array of arrays. in C, it's a continuous chunk of memory. Another example, Java treats all ints as 8-byte values, where C has 1-, 2-, 4-, and 8-byte values (char, short, long,and long long).

Similar to network programming, it is best to keep integer sizes specific. Although most systems do implement ints as 4-byte values, in the interests of portability (especially in these days of moving from 32-bit to 64-bit machines), it is best to be more specific in the definition of integers (use long or long long instead of int).

JNI provides the means to pull the data out of the Java structures. For example, matlab requires everything in a two-dimensional array of doubles.

Java implementation
public static native boolean insertVariable( String name, double[][] matrix );
Native C implementation
#include  <stdint.h>
#include  <stdio.h>
#include  <stdlib.h>
#include  <sys/types.h>

#include  <engine.h>                    // Matlab engine.
#include  "MatlabImpl_NATIVE.h"         // Java header.

[ ... ]

    	JNIEXPORT jboolean JNICALL 
Java_edu_ucsb_cs_jicos_services_external_services_matlab_MatlabImpl_insertVariable(
    JNIEnv* jniEnv,
    jclass  jThis,
    jstring jVarName,
    jobjectArray jMatrix
){
    [ ... ]

    // Get the dimensions of the array.
    numRows = (*jniEnv)->GetArrayLength( jniEnv, jMatrix );
    oneDim = (jdoubleArray)((*jniEnv)->GetObjectArrayElement( jniEnv, jMatrix, 0 ));
    numCols = (*jniEnv)->GetArrayLength( jniEnv, oneDim );


    // Allocate the memory
    if( NULL != (matrix = mxCreateDoubleMatrix( numRows, numCols, mxREAL )) )
    {
	double*  dPtr = mxGetPr( matrix );
	
	// for each row
	for( r=0; r<numRows; ++r )
	{
	    oneDim = (jdoubleArray)(*jniEnv)->
					GetObjectArrayElement( jniEnv, jMatrix, r );
	    if( NULL != oneDim )
	    {
		// get the column
		input = (*jniEnv)->GetDoubleArrayElements( jniEnv, oneDim, 0 );
		if( NULL != input )
		{
		    for( c=0; c<numCols; ++c, ++input, ++dPtr )
		    {
			*dPtr = (double)*input;
		    }
		}
	    }
	}
    }

    ... put it into the Matlab engine ...
}

[ ... ]

Note that there are two complete copies of the matrix. One in Java, and one in C (placed into the Matlab engine). In addition, the Matlab engine may create other data structures during the execution. This places a strict memory constraint on the size of the matrix allowed. Exceeding this value will cause an Error (not an Exception!) and cause the JVM to exit.

 

Getting information out of Matlab

[ TOP ]

Getting the information out of Matlab is a similar problem to putting the information into Matlab. Since Java has a Garbage Collector (GC), where C does not, any data passed back into Java must be under control of the GC. Although JNI has the methods available for creating new Java objects, it is often easier to create the objects on the Java side, and pass them over to the native side, and let them populate them.

For example, the result of the Matlab computation is placed into a ResultObject inner class. The native code populates the values, according to the result (often resulting in yet another matrix).