Commit 06ca5585 authored by Ryan Berkheimer's avatar Ryan Berkheimer

completed factoring of list,type,jni,and packet methods from endpoint class,...

completed factoring of list,type,jni,and packet methods from endpoint class, tests show successfully unchanged behavior. this leaves protocolrecord, record, rejection, condition, field, and endpoint left to factor before ready to implement sessions, conditions, and transformations.
parent 09f537cc
Pipeline #5760 failed with stages
in 0 seconds
......@@ -63,16 +63,16 @@ FF=gfortran
#The following vars contain strings representing common libraries used by all code
#at a project level. These should generally not be touched by users.
COMMONC=
COMMONCPP=MessageApiEndpoint.cpp MessageApiEndpointLib.cpp
COMMONCPP=JniUtils.cpp TypeUtils.cpp ListUtils.cpp PacketUtils.cpp MessageApiEndpoint.cpp MessageApiEndpointLib.cpp
COMMONFORTRAN=
JAVACLASSES:=$(PROJECTPATH)/src/java/main/gov/noaa/messageapi/endpoints/NativeEndpoint.java
JAVAPATH=$(PROJECTPATH)/src/java/main:$(PROJECTPATH)/src/java/main/gov/noaa/messageapi/endpoints
#The following contains source code paths derived from the ENDPOINT_NAME and ENDPOINT_TYPE supplied by the user.
COMMONCPATH=$(PROJECTPATH)/src/c/main/common/endpoint/
COMMONCPATH=$(PROJECTPATH)/src/c/main/common/structs/
USERCPATH=$(PROJECTPATH)/src/c/$(ENDPOINT_TYPE)/endpoints/$(ENDPOINT_NAME)/
COMMONCPPPATH=$(PROJECTPATH)/src/cpp/main/common/endpoint/
COMMONCPPPATH=$(PROJECTPATH)/src/cpp/main/common/
USERCPPPATH=$(PROJECTPATH)/src/cpp/$(ENDPOINT_TYPE)/$(ENDPOINT_NAME)/
COMMONFORTRANPATH=$(PROJECTPATH)/src/fortran/main/common/
USERFORTRANPATH=$(PROJECTPATH)/src/fortran/$(ENDPOINT_TYPE)/$(ENDPOINT_NAME)/
......
### This is a Makefile for MessageAPI Native Endpoints.
# The majority of this Makefile is standard, with very few user specified things.
# There are a few specific requirements and other notable things to keep in mind when using this Makefile:
## 1. You must have a JDK installed (not JRE). This library requires the jni.h header included in your jdk.
## 2. JAVA_HOME must be set to your JDK home directory. This Makefile references JAVA_HOME in looking for jni.h
## 3. This Makefile assumes a specific project layout when setting the PROJECTPATH. If the Makefile is not working,
## you should look at how it uses PROJECTPATH, ENDPOINT_NAME, and ENDPOINT_TYPE to find required files.
## if your project is set up differently, i.e. custom project layout, then path dependent vars will need to be
## looked at and possibly changed.
#The following represents the root project path. DO NOT EDIT
SCRIPTDIR=$(PWD)
PROJECTPATH=$(firstword $(subst /scripts/$(ENDPOINT_TYPE)/, ,$(SCRIPTDIR)))
#The following is used to determine how native resources are compiled. DO NOT EDIT.
UNAME := $(shell uname)
###################################################### USER SPECIFIED SECTION - PLEASE EDIT ###########################
#USER SPECIFIED
#The following vars are used to route source code and scripts (from the scripts and src dirs)
#to the library dir as compiled files (the job name should match the name of the directory/job map.)
#the job type is simply a test job or a main job and should either be test or main.
ENDPOINT_NAME=demosession
ENDPOINT_TYPE=test
#USER SPECIFIED
#The following var represents the output name of the JniLib that will be created and referenced in the job map.
#Do not specify the extension (.so/.dll/.jnilib) - these are determined automatically on build.
#The full path to the created library must be referenced in the endpoint config.
LIBNAME=DemoSession
#USER SPECIFIED
#The following vars should contain strings representing the C, Fortran, and CPP libraries that glue the fortran code to java code.
#These strings should contain the user libraries in the order they need to be compiled (ex, "First.c Second.c Third.c")
#These vars do not need to specify libraries they depend on that are found in the common variables (those are automatically compiled first).
USERC=EndpointWrapper.c
USERCPP=
USERFORTRAN=
###################################################### END USER SPECIFIED SECTION ###########################
#The following represent directories for JNI Libraries that must be included during C and CPP compilation.
#These are platform dependent. If you have a default setup, these probably will not have to be modified.
#However, if you have nonstandard include paths, or you are getting errors, this may need to be altered.
ifeq ($(UNAME), Linux)
#The following represent a standard RHEL 7 system.
JNIDIR="$(JAVA_HOME)/include"
JNIMDDIR="$(JAVA_HOME)/include/linux"
JVMHEADERS="$(JAVA_HOME)/include/linux"
endif
ifeq ($(UNAME), Darwin)
#The following represent a standard OSX system.
JNIDIR="$(JAVA_HOME)/include"
JNIMDDIR="$(JAVA_HOME)/include/darwin"
JVMHEADERS="$(JAVA_HOME)/include"
endif
#compilers
CXX=g++
CC=gcc
FF=gfortran
#The following vars contain strings representing common libraries used by all code
#at a project level. These should generally not be touched by users.
COMMONC=
COMMONCPP=MessageApiEndpoint.cpp MessageApiEndpointLib.cpp
COMMONFORTRAN=
JAVACLASSES:=$(PROJECTPATH)/src/java/main/gov/noaa/messageapi/endpoints/NativeEndpoint.java
JAVAPATH=$(PROJECTPATH)/src/java/main:$(PROJECTPATH)/src/java/main/gov/noaa/messageapi/endpoints
#The following contains source code paths derived from the ENDPOINT_NAME and ENDPOINT_TYPE supplied by the user.
COMMONCPATH=$(PROJECTPATH)/src/c/main/common/structs/
USERCPATH=$(PROJECTPATH)/src/c/$(ENDPOINT_TYPE)/endpoints/$(ENDPOINT_NAME)/
COMMONCPPPATH=$(PROJECTPATH)/src/cpp/main/common/endpoint/
USERCPPPATH=$(PROJECTPATH)/src/cpp/$(ENDPOINT_TYPE)/$(ENDPOINT_NAME)/
COMMONFORTRANPATH=$(PROJECTPATH)/src/fortran/main/common/
USERFORTRANPATH=$(PROJECTPATH)/src/fortran/$(ENDPOINT_TYPE)/$(ENDPOINT_NAME)/
#The following var contains the target path for the project code.
TARGETPATH="$(PROJECTPATH)/lib/$(ENDPOINT_TYPE)/native/$(ENDPOINT_NAME)/"
#The following is the target library for the native jni library.
ifeq ($(UNAME), Linux)
#The following represents a standard RHEL 7 system.
LNFLAGS=-dynamiclib -shared -lgfortran -I$(JVMHEADERS)
endif
ifeq ($(UNAME), Darwin)
#The following represents a standard OSX system.
LNFLAGS=-dynamiclib -shared -lgfortran -I$(JVMHEADERS) -framework JavaVM
endif
CFLAGS=-I$(JVMHEADERS) -I$(COMMONCPPPATH) -I$(COMMONCPATH) -I$(USERCPPPATH) -I$(USERCPATH) -I$(JNIDIR) -I$(JNIMDDIR) -fpic -std=c99
FFLAGS=-I$(COMMONFORTRANPATH) -I$(USERFORTRANPATH) -fPIC -fopenmp
#The following is the target library for the native jni library.
ifeq ($(UNAME), Linux)
#The following represents a standard RHEL 7 system.
DYLIB=lib$(LIBNAME).so
endif
ifeq ($(UNAME), Darwin)
#The following represents a standard OSX system.
DYLIB=lib$(LIBNAME).jnilib
endif
#COMMONFORTRAN1=$(addprefix $(COMMONFORTRANPATH), $(COMMONFORTRAN))
#USERFORTRAN1=$(addprefix $(USERFORTRANPATH), $(USERFORTRAN))
COMMONC1=$(addprefix $(COMMONCPATH), $(COMMONC))
USERC1=$(addprefix $(USERCPATH), $(USERC))
COMMONCPP1=$(addprefix $(COMMONCPPPATH), $(COMMONCPP))
USERCPP1=$(addprefix $(USERCPPPATH), $(USERCPP))
all: clean gen-headers build
.PHONY : clean gen-headers build
gen-headers:
echo "Generating headers. "
echo ""
$(foreach header,$(JAVACLASSES),javac -h $(USERCPATH) -classpath $(JAVAPATH) $(header);)
echo ""
build:
echo "Building " $(ENDPOINT_NAME) " in " $(PROJECTPATH)
#echo "Compiling Fortran"
#echo ""
#cd $(TARGETPATH) && $(FF) $(FFLAGS) -c $(COMMONFORTRAN1) $(USERFORTRAN1)
#echo ""
echo "Compiling CPP"
echo ""
cd $(TARGETPATH) && $(CXX) $(CFLAGS) -c $(COMMONCPP1) $(USERCPP1)
echo ""
echo "Compiling C"
echo ""
cd $(TARGETPATH) && $(CC) $(CFLAGS) -c $(COMMONC1) $(USERC1)
echo ""
echo "Creating JNI Library"
echo ""
cd $(TARGETPATH) && $(CXX) $(LNFLAGS) *.o -o $(DYLIB)
echo "Build success for " $(ENDPOINT_NAME)
clean:
echo "Cleaning up."
echo ""
-cd $(TARGETPATH) && rm *.h
-cd $(TARGETPATH) && rm *.o
-cd $(TARGETPATH) && rm *.mod
-cd $(TARGETPATH) && rm *.jnilib
-cd $(TARGETPATH) && rm *.so
echo ""
/**
This header contains the definitions
for all structs used by the native messageAPI
library.
These structs correspond to the interfaces
available for interaction in the Java portion
of the library. These structs should not
ever need to change - any native library
should be able to completely interact with
MessageAPI.
The struct surface consists of:
-field
-condition
-record
-rejection
-request
-response
-packet
-session
@author Ryan Berkheimer
*/
/**
The field is a basic value-holding struct.
It should only hold one 'type' of value.
The value methods associated with this struct
operate on void pointers and must be cast
by the caller depending on the 'type' property
of the field.
The library methods that the field struct is related to are
char* messageapi_field_getId(field *f);
char* messageapi_field_getType(field *f);
void* messageapi_field_getValue(field *f);
bool messageapi_field_isRequired(field *f);
bool messageapi_field_isValid(field *f);
bool messageapi_field_setValid(field *f, bool isValid);
void messageapi_field_setType(field* f, char *type);
void messageapi_field_setValue(field *f, void *value);
*/
struct field
{
char *id;
char *type;
bool isRequired;
bool isValid;
void *value;
};
/**
The condition is used in filtering,
belonging to both records and requests.
Conditions are defined outside of code,
with values being able to be set inside
or outside of code. Like Fields, conditions
define a specific type that is used in validation on
value assignment. Values are both set and retrieved
as void pointers, so must be cast by the user based on
the type.
char* messageapi_condition_getId(condition *c);
char* messageapi_condition_getType(condition *c);
char* messageapi_condition_getOperator(condition *c);
void* messageapi_condition_getValue(condition *c);
char* messageapi_condition_setValue(condition *c, void *value);
*/
struct condition
{
char *id;
char *type;
char *oprtr;
void *value;
};
/**
The record struct holds a list
of fields and a list of conditions, and a
boolean indicating if the record is valid or not.
The record is related to several library methods that
provide access to its members, along with another
method that allows getting a full copy of the record.
The library methods that record relates to are:
field** messageapi_record_getFields(record *r);
field* messageapi_record_getField(record *r, char* fieldId);
bool messageapi_record_hasField(record *r, char* fieldId);
condition** messageapi_record_getConditions(record *r);
condition* messageapi_record_getCondition(record *r, char* conditionId);
record* messageapi_record_getCopy(record *r);
void messageapi_record_setFields(record *r, field** fields);
void messageapi_record_setField(record *r, void* field, void* value);
void messageapi_record_setConditions(record *r, condition** conditions);
void messageapi_record_setCondition(record *r, void* condition, void* value);
bool messageapi_record_isValid(record *r);
void messageapi_record_setValid(record *r, bool valid);
*/
struct record
{
field **fields;
condition **conditions;
bool isValid;
};
/**
The rejection struct holds a reference to the record
that was rejected, and a list of reasons as strings
that explain why the referenced record was rejected.
Library methods that relate to rejections include:
record* messageapi_rejection_getRecord(rejection* r);
rejection* messageapi_rejection_getCopy(rejection* r);
char** messageapi_rejection_getReasons(rejection* r);
void messageapi_rejection_addReason(rejection* r, char* reason);
*/
struct rejection
{
record *record;
char **reasons;
};
/**
The request struct holds a list of records and its own
request record (which holds request-wide conditions).
Requests, derived from Sessions (holding a deep copy of
the session schema, container, and protocol), are the primary
vector of process formation and execution in MessageAPI.
Requests are submitted directly and immediately return a response
asynchronously. Requests can be reused, with each submit() call
applying to the same endpoint state.
Library methods that relate to requests include:
record* messageapi_request_createRecord(request* r);
request* messageapi_request_getCopy(request* r);
record** messageapi_request_getRecords(request* r);
void messageapi_request_setCondition(char* id, void* value);
void messageapi_request_setRecords(request* request, record** records);
response* messageapi_request_submit(request* r);
*/
struct request
{
record **records;
record **requestRecord;
};
/**
The response struct primarily holds
the initiating request, a list of records,
and a list of rejections. It also holds a
boolean value isComplete that specifies
whether or not the processing that takes place
in the response has finished, and that the records
and rejections are available for consumption.
Library methods that relate to responses include:
request* messageapi_response_getRequest(response* r);
bool messageapi_response_isComplete(response* r);
record** messageapi_response_getRecords(response* r);
rejection** messageapi_response_getRejections(response* r);
*/
struct response
{
request* request;
bool isComplete;
record** records;
rejection** rejections;
};
/**
The packet struct holds a list of records
and a list of rejections. It is used as a
return for all endpoint connections.
Library methods that relate to packets include:
void messageapi_packet_setRecords(packet* p, records** rs);
void messageapi_packet_addRecord(packet* p, record* r);
void messageapi_packet_addRecords(packet* p, record** rs);
record** messageapi_packet_getRecords(packet* p);
void messageapi_packet_setRejections(packet* p, rejections** rs);
void messageapi_packet_addRejection(packet* p, rejection* r);
void messageapi_packet_addRejections(packet* p, rejections** rs);
record** messageapi_packet_getRejections(packet* p);
*/
struct packet
{
record** records;
rejection** rejections;
};
/**
The session struct holds the ability to create
requests. It creates these requests using a definition
of a schema, container, and protocol.
Library methods that relate to packets include:
request* messageapi_create_request(const char* sessionManifest);
*/
struct session
{
};
#ifndef _Included_messageapi_structs
#define _Included_messageapi_structs
#include <jni.h>
/**
* @author Ryan Berkheimer
*/
struct list_item
{
jobject jitem;
};
struct list_entry
{
jobject jentry;
......@@ -79,4 +88,6 @@ struct classifier
{
char* key;
char* val;
};
\ No newline at end of file
};
#endif
\ No newline at end of file
#include <jni.h>
#include <stdio.h>
#include "endpoint_structs.h"
#include "messageapi_structs.h"
#include "MessageApiEndpointLib.h"
#include "gov_noaa_messageapi_endpoints_NativeEndpoint.h"
......@@ -57,7 +57,7 @@ JNIEXPORT jobject JNICALL Java_gov_noaa_messageapi_endpoints_NativeEndpoint_proc
struct val_list *val_list = getFieldListVal(message, testField3);
printf("Field value 3 (list) length is: %d\n", val_list->count);
for (int i = 0; i < val_list->count; i++) {
printf("Field value 3, element %d, is: %d\n", i, getIntEntry(message, val_list, i));
printf("Field value 3, element %d, is: %d\n", i, getIntItem(message, val_list, i));
}
fflush(stdout);
if (getFieldIsNull(message, testField4)) {
......@@ -82,12 +82,14 @@ JNIEXPORT jobject JNICALL Java_gov_noaa_messageapi_endpoints_NativeEndpoint_proc
}
}
fflush(stdout);
printf("Creating a string list\n");
printf("Creating a string list..\n");
struct val_list *return_val_list = createList(message);
addStringEntry(message, return_val_list, "first element of our string!");
addStringEntry(message, return_val_list, "second element of our string!!");
addStringItem(message, return_val_list, "first element of our string!");
addStringItem(message, return_val_list, "second element of our string!!");
printf("Created a string list.\n");
setFieldListVal(message, getField(message, returnRecord, "return-list"), return_val_list);
printf("First value added to the return-list field: %s\n", getStringItem(message, return_val_list, 0));
printf("Second value added to the return-list field: %s\n", getStringItem(message, return_val_list, 1));
printf("Added the string list to the return record. Should have two elements, see above.\n");
fflush(stdout);
struct packet* packet = createPacket(message);
......
#include <jni.h>
#include <stdio.h>
#include "messageapi_structs.h"
#include "MessageApiSessionLib.h"
int main(int argc, char **argv)
{
JavaVM *vm;
JNIEnv *env;
JavaVMInitArgs vm_args;
jint res;
jclass cls;
jmethodID mid;
jstring jstr;
jobjectArray main_args;
//vm_args.version = JNI_V
vm_args.nOptions = 0;
res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
if (res != JNI_OK)
{
printf("Failed to create Java VMn");
return 1;
}
cls = (*env)->FindClass(env, "Main");
if (cls == NULL)
{
printf("Failed to find Main classn");
return 1;
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
if (mid == NULL)
{
printf("Failed to find main functionn");
return 1;
}
jstr = (*env)->NewStringUTF(env, "");
main_args = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), jstr);
(*env)->CallStaticVoidMethod(env, cls, mid, main_args);
return 0;
}
\ No newline at end of file
#include "JniUtils.h"
namespace JniUtils
{
void checkAndThrow(JNIEnv *jvm, std::string errorMessage)
{
if (jvm->ExceptionCheck())
{
std::cout << "MessageApiEndpoint.cpp errored with the following message:" << errorMessage << std::endl;
jclass jException = static_cast<jclass>(jvm->NewGlobalRef(jvm->FindClass("java/lang/Exception")));
jvm->ThrowNew(jException, errorMessage.c_str());
jvm->DeleteLocalRef(jException);
}
}
jclass getNamedClass(JNIEnv *jvm, const char *className)
{
jclass clazz = jvm->FindClass(className);
const char *msg = "getNamedClass> failed for class:";
std::string buf(msg);
buf.append(className);
checkAndThrow(jvm, buf);
return clazz;
}
jclass getGlobalClassRef(JNIEnv *jvm, const char *classname)
{
return static_cast<jclass>(jvm->NewGlobalRef(jvm->FindClass(classname)));
}
jclass getObjectClass(JNIEnv *jvm, jobject obj)
{
jobject objRef = jvm->NewLocalRef(obj);
jclass clazz = jvm->GetObjectClass(objRef);
jvm->DeleteLocalRef(objRef);
const char *msg = "getObjectClass> failed.";
std::string buf(msg);
checkAndThrow(jvm, buf);
return clazz;
}
jmethodID getMethod(JNIEnv *jvm, jclass clazz, const char *name, const char *signature, bool isStatic)
{
jmethodID id = NULL;
if (isStatic)
{
id = jvm->GetStaticMethodID(clazz, name, signature);
}
else
{
id = jvm->GetMethodID(clazz, name, signature);
}
const char *msg = "getMethod> jMethodID lookup failed.";
std::string buf(msg);
buf.append(name);
checkAndThrow(jvm, buf);
return id;
}
} /*namespace JniUtils*/
\ No newline at end of file
#ifndef _JNIUTILS_H
#define _JNIUTILS_H
#include <jni.h>
#include <string.h>
#ifdef __cplusplus
#include <iostream>
#include <stdlib.h>
/**
* The JniUtils namespace contains methods for general use for JNI interoperability,
* such as retrieving classes, method ids, and throwing java errors.
*/
namespace JniUtils
{
/*Handles errors allowing throws in java code.*/
void checkAndThrow(JNIEnv *jvm, std::string errorMessage);
/*Return a jclass for the provided class name and the jvm context. The passed jvm must have the class loaded already in the classpath.*/
jclass getNamedClass(JNIEnv *jvm, const char *javaClassName);
/*Returns a global class ref from JNI using a static cast. The global ref needs to be explicitly removed.*/
jclass getGlobalClassRef(JNIEnv *jvm, const char *classname);
/*Return a jclass for the provided jobject held by the provided jvm context.*/
jclass getObjectClass(JNIEnv *jvm, jobject javaObject);
/*Lookup a java methodID for the given class, method name, and signature string. Also pass in whether method is class static or not.*/
jmethodID getMethod(JNIEnv *jvm, jclass javaClass, const char *methodName, const char *methodSignature, bool isStatic);
} /*namespace JniUtils*/
extern "C"
{
#endif
#ifdef __cplusplus
}
#endif
#endif
#include "ListUtils.h"
#include "JniUtils.h"
ListUtils::ListUtils(JNIEnv *env, TypeUtils *typeUtils)
{
this->loadGlobalRefs(env, typeUtils);
this->loadMethodIds();
}
ListUtils::~ListUtils()
{
try
{
}
catch (const std::exception &e)
{
std::cout << e.what();
}
}
void ListUtils::loadGlobalRefs(JNIEnv *env, TypeUtils *typeUtils)
{
this->jvm = env;
this->typeUtils = typeUtils;
}
void ListUtils::loadMethodIds()
{
jclass listClass = JniUtils::getNamedClass(this->jvm, "java/util/List");
this->getListSizeMethodId = this->jvm->GetMethodID(listClass, "size", "()I");
this->getListItemMethodId = this->jvm->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
this->addListItemMethodId = this->jvm->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
this->jvm->DeleteLocalRef(listClass);
this->createListMethodId = this->jvm->GetMethodID(this->typeUtils->getListClass(), "<init>", "()V");
}
jmethodID ListUtils::createListMethod()
{
return this->createListMethodId;
}
jmethodID ListUtils::getListSizeMethod()
{
return this->getListSizeMethodId;
}
jmethodID ListUtils::getListItemMethod()
{
return this->getListItemMethodId;
}
jmethodID ListUtils::addListItemMethod()
{
return this->addListItemMethodId;
}
struct val_list *ListUtils::createList()
{
jobject jList = this->jvm->NewObject(this->typeUtils->getListClass(), this->createListMethod());
struct val_list *valueList = (struct val_list *)malloc(sizeof(struct val_list));
valueList->count = 0;
valueList->jlist = jList;
return valueList;