From GridLAB-D Wiki
Jump to: navigation, search

 TODO:  This page needs revision and updating for Hassayampa (Version 3.0)

The Java module loader is a recent addition to the GridLAB-D project. It consists of a module that loads a JVM, several .class files that include native function calls, handles for wrapping core-implemented objects and accessing their C-space members, and some sample .java files.

Definitions

DLL 
dynamically linked library. Compare to *NIX shared objects (.so).
Java module 
a GridLAB-D module written in the Java programming language and compiled into bytecode. Must include specific methods and implement specific behaviors akin to modules written in C.
JVM 
Java virtual machine.
object member 
a variable defined in OBJECT*, such as the name, rank, or clock.
object property 
a dynamically defined and allocated variable stored within the extended OBJECT* malloc block, constructed using gl_publish_variable().

GridLAB-D conventions

This document uses the term "GridLAB-D" to refer to the simulator. There is also a GridlabD.class, which is written as "GridlabD" in this document, since a dash is not part of a valid class name.

What’s Needed

In order to load a Java module into GridLAB-D, the core needs the glmjava module loaded before any Java modules, a stub C module that provides symbols for the core's function pointers to reference, and the compiled class files for the JVM to load.

Loading the glmjava module is a straightforward process. Include the directive module glmjava; before attempting to load any Java modules. Along with initializing the JVM, this will provide module getvar's to jcallback, jvm, and jnienv, which will return pointers to the glmjava callback structure, a pointer to the loaded JVM, and a JNIEnv* for the current thread, respectively. The DLL contains the symbols for the native methods referenced in the GridlabD and GObject classes, which link back to the core callbacks, and enable reading and writing both object members and object properties from Java.

The original method of loading Java modules also required a stub module written in C. The stub module needs to include at least two files. The main.cpp file can be copied from Appendix A. The init.cpp file can be copied from Appendix B, but requires that end users fill in MODULENAME with the name of the Java module class, which will be explained below, and includes EXPORT_JAVA_* calls to creates the call-through functions that allow the core to snake program control into Java.

The glmjava DLL

The GridLAB-D core does not include or know anything about Java – if it did, every other C native module would need to include jni.h, whether or not that module used Java. Importing Java modules requires a Java virtual machine, however, so we load that in an external module along with a set of callbacks for accessing the JVM, and a set of functions that interface the Java GridlabD.* methods to their corresponding gl_* macro functions.

Including the directive module glmjava; will cause GridLAB-D to load the Java virtual machine, which will allow users to load

The C Stub DLL

Version 2.0: The stub DLL is no longer needed for loading Java modules. To load a java module, follow the “module glmjava;” line with a “module glmjava::JavaModuleClass;” call, where JavaModuleClass.class contains a “public static long init(long, String, int String[])” that returns the address of the first registered class.

The GridLAB-D core relies on locating function symbols for referencing the init, create, sync, and other class and module related calls.

There are two sets of macros used heavily within the stub modules. The first, straightforwards one is to #define MODULENAME "YourModuleNameHere". This string must equal the name of the module class that contains the init() call, and any other module-related calls, including do_kill(), check(), import_file(), export_file(), setvar(), module_test(), cmdargs(), and kmldump(). Although init() is required to be present, the remaining functions will be ignored if they are not present in the Java class.

The second batch of macros include EXPORT_JAVA_CREATE(ClassNameHere), EXPORT_JAVA_SYNC(), EXPORT_JAVA_INIT(), EXPORT_JAVA_PLC(), etc. These will create call-through functions that will look for and call ClassNameHere.create(), etc, that the core needs to execute object behavior.

The GridlabD Package

A set of classes have been included to wrap the process of registering classes and constructing objects with the core. The noteworthy classes are GridlabD, GModule, GClass, GObject, and GProperty. The latter four mirror the MODULE, CLASS, OBJECT, and PROPERTY structures within the core, and contain methods that wrap the GridlabD.register_class(), GridlabD.publish_variable(), and GridlabD.create_object() calls. These objects also include dictionaries of what properties are contained in each class, along with what object instances have been created within the core for that type, keyed by OBJECT*.

The full list of supported GridLAB-D calls can be found in Appendix C.

Java Caveats

GridLAB-D is written in C and heavily leverages pointers. It is impossible for any module to get away from these pointers, and those written in Java are no exception. The GridlabD package handles pointers as long values, and uses those address values for get and set functions, for dictionary keys, and to reference objects within the core, such as MODULE*, OBJECT*, and PROPERTY*. Developers need to be very careful about what values their module hands to long arguments. Passing in "bad" values to a method can return garbage, write garbage into odd memory locations, or crash GridLAB-D with a segmentation fault.

Other GridLAB-D objects are only able to access data in published variables.

Constructing the Java Module

In this context, “module” is a bit of a misnomer, as it is completely reasonable to include independent class files instead of a single jar’ed package. The interface for GridLAB-D must include a public static int init(long, long) method, and should include a private static GModule member. The program control will flow into the init method, where the module needs to register all of its classes and publish all the variables those classes will use. It must be noted that class variables can only be published prior to the first instantiation of a class. Using the GClass.BuildSingle method allocates a memory block in the core, and any change to the property list will cause buffer overflows and trample adjacent structures.

The names of the classes published needs to correspond to the names of implemented classes, and that have export macro calls in the module’s corresponding C stub DLL. The published variables needn’t be named similar to anything on the C or Java side, but developers should avoid using the keywords “name”, “clock”, “latitude”, “longitude”, “in_svc”, “out_svc”, “rank”, “parent”, “id”, and “flags”. These are used by the core and fields published with these names will behave strangely. Published classes and variables should also have internally unique names; where it will not conflict with two different classes publishing a variable “foo”, attempting to republish “foo” will fail. While two different modules can publish a “start_here” class, objects would need to be created using an explicit “module::start_here” in GLM files.

Caveat: use of jar files has not been tested as of 7 March 2008.

Constructing Java Classes for GridLAB-D

Java classes written to work with GridLAB-D must include a public static long create(long) method and a public static long sync(long, long, int) method. The create method must return the address value of a GObject, whether inherited or composited. The sync method needs to return the next time_t that it needs to be updated, or GridlabD.TS_NEVER if it has no further events or has reached a steady state. A public static void register(GClass) can be a useful method for collecting the publish_variable calls and to cache the GClass object, which is referenced by the GObject class when GridlabD objects are constructed.


Appendix A – main.cpp

#define MAJOR 1
#define MINOR 0
#define DLMAIN

#include <stdlib.h>
#include "gridlabd.h"

EXPORT /*CDECL __declspec(dllexport)*/ int do_kill(void*);
EXPORT /*CDECL __declspec(dllexport)*/ int major=MAJOR, minor=MINOR;

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
#include <windows.h>
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID)
{
        switch (ul_reason_for_call)
        {
                case DLL_PROCESS_ATTACH:
                case DLL_THREAD_ATTACH:
                        break;
                case DLL_THREAD_DETACH:
                case DLL_PROCESS_DETACH:
                        do_kill(hModule);
                        break;
        }
        return TRUE;
}
#else // !WIN32
CDECL int dllinit() __attribute__((constructor));
CDECL int dllkill() __attribute__((destructor));
CDECL int dllinit() { return 0;}
CDECL int dllkill() { do_kill(NULL);}
#endif // !WIN32

/* EOF */

Appendix B – init.cpp

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <direct.h>
#include <jni.h>

#include "gridlabd.h"
#include "gridlabd_java.h"
#include "javamod.h"

#define MODULENAME "SampleModule"
#define MAJOR 1
#define MINOR 0

static JAVACALLBACKS *jcallback;
static JavaVM *jvm = NULL;

EXPORT_JAVA_CREATE(SampleClass)
EXPORT_JAVA_INIT(SampleClass)
EXPORT_JAVA_SYNC(SampleClass)
EXPORT_JAVA_PLC(SampleClass)

EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *argv[]){
        MODULE *mod_java = (*fntable->module_find)("glmjava");
        if(mod_java != NULL)
                return java_init(fntable, (JAVACALLBACKS *)(*fntable->get_module_var)(mod_java, "jcallback"), module, argc, argv);
        else {
                (*fntable->output_error)("Module %s unable to find module glmjava -- please load the glmjava module first!", MODULENAME);
                return NULL;
        }
}

CLASS *java_init(CALLBACKS *fntable, JAVACALLBACKS *jfntable, MODULE *module, int argc, char *argv[])
{
        JNIEnv *jnienv = NULL;
        if (!set_callback(fntable)) {
                errno = EINVAL;
                return NULL;
        }
        jcallback = jfntable;
        if(jcallback == NULL){
                gl_error("%s:java_init() - unable to find jcallback");
                return NULL;
        }
        if(jvm == NULL)
                jvm = jgl_get_jvm();
        if(jnienv == NULL)
                jnienv = jgl_get_env();
        jstring *jargv = new jstring[argc];
        int i = 0;
        gl_output("javamod init entered\n");

        char b[256];
        printf("getcwd = %s\n", _getcwd(b, 256));

        jclass cls = jnienv->FindClass(MODULENAME);
        if(cls == NULL){
                gl_error("javamod:init.cpp: unable to find %s.class", MODULENAME);
                return NULL;
        }

        jmethodID init_mid = jnienv->GetStaticMethodID(cls, "init", "(JLjava/lang/String;I[Ljava/lang/String;)J");

        if(init_mid == NULL){
                gl_error("javamod:init.cpp: unable to find \"int %s.init(long, string, int, string[])\"", MODULENAME);
                return NULL;
        }

        jobjectArray args = jnienv->NewObjectArray(argc, jnienv->FindClass("[Ljava/lang/String;"), NULL);
        if(args == NULL){
                gl_error("javamod:init.cpp: unable to allocate args[] for %s.init()", MODULENAME);
                return NULL;
        }
        
        for(i = 0; i < argc; ++i){
                jargv[i] = jnienv->NewStringUTF(argv[i]);
                jnienv->SetObjectArrayElement(args, i, jargv[i]);
        }

        jstring jmodname = jnienv->NewStringUTF(MODULENAME);
        if(jmodname == NULL){
                gl_error("javamod:init.cpp: unable to allocate jmodname for %s.init()", MODULENAME);
        }

        gl_verbose("javamod:init.cpp(): moduleaddr = %x", module);

        int64 rv = jnienv->CallStaticLongMethod(cls, init_mid, (int64)module, jmodname, argc, jargv);
        if (jnienv->ExceptionOccurred()) {
                jnienv->ExceptionDescribe();
        }
        
        // JNI cleanup
        jnienv->DeleteLocalRef(args);
        for(i = 0; i < argc; ++i)
                ; /* delete the strings */

        gl_output("finished javamod init\n");

        return (CLASS *)rv;
}

CDECL int do_kill()
{
        // if anything needs to be deleted or freed, this is a good time to do it
        return 0;
}

Appendix C - GridlabD static calls

int verbose(String str)
int output(String str)
int warning(String str)
int error(String str)
int debug(String str)
int testmsg(String str)
long malloc(int size)
long get_module_var(String modulename,  String varname)
String findfile(String filename)
long find_module(String modulename)
long register_class(long moduleaddr, String classname, int passconfig)
long create_object(long oclass_addr, long size)
long create_array(long oclass_addr, long size, int n_objects)
int object_isa(long obj_addr, String typename)
long publish_variable(long moduleaddr, long classaddr, String varname, String vartype, long offset)
long publish_function(String modulename, String classname, String funcname)
int set_dependent(long from_addr, long to_addr)
int set_parent(long child_addr, long parent_addr)
long register_type(long oclass_addr, String typename, String from_string_fname, String to_string_fname)
int publish_delegate()
long get_property(long objaddr, String propertyname)
long get_property_by_name(String objectname, String propertyname)
int get_value(long object_addr, String propertyname, String value)
int set_value(long object_addr, String propertyname, String value)
double unit_convert(String from, String to, double invalue)
GridlabD.Complex get_complex(long object_addr, long property_addr)
GridlabD.Complex get_complex_by_name(long object_addr, String propertyname)
long get_object(String oname)
int get_int16(long object_addr, long property_addr)
int get_int16_by_name(long object_addr, String propertyname)
int get_int32(long object_addr, long property_addr)
int get_int32_by_name(long object_addr, String propertyname)
int get_int64(long object_addr, long property_addr)
int get_int64_by_name(long object_addr, String propertyname)
double get_double(long object_addr, long property_addr)
double get_double_by_name(long object_addr, String propertyname)
String get_string(long object_addr, long property_addr)
String get_string_by_name(long object_addr, String propertyname)
long[] find_objects(long findlist_addr, String[] args)
native long find_next(long findlist_addr)
long create_aggregate(String aggregator, String group_expression)
double run_aggregate(long aggregate_addr)
double random_uniform(double a, double b)
double random_normal(double m, double s)
double random_lognormal(double m, double s)
double random_bernoulli(double p)
double random_pareto(double m, double a)
double random_sampled(int n, double[] x)
double random_exponential(double l)
long parsetime(String value)
int strtime(Object timestamp, String buffer, int size)
double todays(long t)
double tohours(long t)
double tominutes(long t)
int localtime(long t, Object datetime)
long global_create(String name, String args)
int global_setvar(String def, String args)
String global_getvar(String name, String buffer, int size)
long global_find(String name)