Accelerator Independent Data Access / PVAccess 2.0
AIDA-PVA is the latest version of the AIDA framework. Built on top of EPICS 7 it enables client applications to programmatically access and manage any device or database on the SLAC Network using simple channel names.
Loading...
Searching...
No Matches
aida_pva_jni_helper.c
Go to the documentation of this file.
1/** @file
2 * @brief JNI Helper provides all the boilerplate JNI processing required. It implements all the JNI
3 * parameter marshalling and return value creation. None of these functions should be called
4 * by the Native Channel Provider because they are further abstracted away by the
5 * aida_server_helper.
6 *
7 * **MEMBER**=SLCLIBS:AIDA_PVALIB
8 * **ATTRIBUTES**=JNI
9 *
10 */
12#include "aida_pva_jni_helper.h"
13
14static int getArgumentClasses(JNIEnv* env, ArgumentClasses* argumentClasses);
15static void getArgumentClassMethods(JNIEnv* env, ArgumentMethods* argumentMethods);
16static int checkMethods(JNIEnv* env, ArgumentMethods* argumentMethods);
17static int allocateSpaceForArguments(JNIEnv* env, Arguments* cArgs, int totalFloatingPoints);
18
19/**
20 * Create a new java object
21 * @param env environment
22 * @param class class of object to create
23 * @return the new jni object
24 */
25jobject newObjectFromClass(JNIEnv* env, jclass class) {
26 jobject object;
27
28 // Get the Method ID of the no-args constructor
29 jmethodID midInit = getConstructorMethodId(env, class);
30 if (!midInit) {
31 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to create object");
32 return NULL;
33 }
34 // Call back constructor to allocate a new instance
35 object = (*env)->NewObject(env, class, midInit);
36
37 return object;
38}
39
40/**
41 * Look up class in the given `env`,
42 * and create a new java object.
43 *
44 * @param env environment.
45 * @param clazz string name of java class to create.
46 * @return the new java object.
47 */
48JavaObject newObject(JNIEnv* env, char* clazz) {
49 JavaObject javaObject;
50 javaObject.class = NULL;
51 javaObject.object = NULL;
52
53 // Get a class reference
54 javaObject.class = (*env)->FindClass(env, clazz);
55 if (!javaObject.class) {
56 char errorString[BUFSIZ];
57 sprintf(errorString, "Failed to create object of class: %s", clazz);
59 return javaObject;
60 }
61 javaObject.object = newObjectFromClass(env, javaObject.class);
62
63 return javaObject;
64}
65
66/**
67 * Convert given jstring to a C string.
68 *
69 * @param env environment.
70 * @param string jstring to convert to a C string.
71 * @return C string.
72 */
73char* toCString(JNIEnv* env, jstring string) {
74 return (char*)(*env)->GetStringUTFChars(env, string, NULL);
75}
76
77/**
78 * Convert C string to jstring.
79 *
80 * @param env environment.
81 * @param string C string.
82 * @return jstring.
83 */
84jstring toJString(JNIEnv* env, const char* string) {
85 if (!string) {
86 return NULL;
87 }
88 return (*env)->NewStringUTF(env, string);
89}
90
91/**
92 * Get the method ID on the given class,
93 * with the given method name,
94 * and signature.
95 *
96 * @param env environment.
97 * @param clazz given class.
98 * @param methodName given method name.
99 * @param methodSignature given signature.
100 * @return the method that matches the name and signature specified.
101 */
102jmethodID getMethodId(JNIEnv* env, jclass clazz, char* methodName, char* methodSignature) {
103 return (*env)->GetMethodID(env, clazz, methodName, methodSignature);
104}
105
106/**
107 * Get the method ID of the constructor of the given class.
108 *
109 * @param env environment.
110 * @param clazz given class.
111 * @return the constructor method id.
112 */
113jmethodID getConstructorMethodId(JNIEnv* env, jclass clazz) {
114 return getMethodId(env, clazz, "<init>", "()V");
115}
116
117/**
118 * Get an {@link Arguments} structure,
119 * from the given java List<{@link AidaArgument}>
120 *
121 * @param env environment.
122 * @param jArguments java arguments list - List<{@link AidaArgument}>
123 * @return {@link Arguments} structure
124 */
125Arguments toArguments(JNIEnv* env, jobject jArguments) {
126 Arguments cArgs;
127 cArgs.argumentCount = 0;
128
129 // Get all classes needed for processing arguments
130 ArgumentClasses argumentClasses;
131 if (getArgumentClasses(env, &argumentClasses)) {
132 return cArgs;
133 }
134
135 // Get class methods
136 ArgumentMethods argumentMethods;
137 argumentMethods.argumentClasses = &argumentClasses;
138
139 getArgumentClassMethods(env, &argumentMethods);
140
141 // Check that the methods were retrieved correctly
142 if (checkMethods(env, &argumentMethods)) {
143 return cArgs;
144 }
145
146 // Get the arguments list
147 jobject argumentsList = (*env)->CallObjectMethod(env, jArguments, argumentMethods.argumentsGetArgumentsMethod);
148
149 // get the size of the list of arguments
150 cArgs.argumentCount = (*env)->CallIntMethod(env, argumentsList, argumentMethods.listSizeMethod);
151
152 // Get list of floats and doubles
153 jobject jFloatsList = (*env)->CallObjectMethod(env, jArguments, argumentMethods.argumentsGetFloatArgumentsMethod);
154 jobject jDoublesList = (*env)->CallObjectMethod(env, jArguments, argumentMethods.argumentsGetDoubleArgumentsMethod);
155
156 // Get the size of the list of floats and doubles
157 int floatCount = jFloatsList ? (*env)->CallIntMethod(env, jFloatsList, argumentMethods.listSizeMethod) : 0;
158 int doubleCount = jDoublesList ? (*env)->CallIntMethod(env, jDoublesList, argumentMethods.listSizeMethod) : 0;
159 int totalFloatingPoints = floatCount + doubleCount;
160
161 // Create space for arguments, allocates space for arguments and the
162 // array of floats / doubles and space for then float/double path names
163 if (allocateSpaceForArguments(env, &cArgs, totalFloatingPoints)) {
164 return cArgs;
165 }
166
167 // walk through and fill array
168 for (int i = 0; i < cArgs.argumentCount; i++) {
169 jobject argument = (*env)->CallObjectMethod(env, argumentsList, argumentMethods.listGetMethod, i);
170 cArgs.arguments[i].name = toCString(env,
171 (*env)->CallObjectMethod(env, argument, argumentMethods.argumentGetNameMethod));
172 cArgs.arguments[i].value = toCString(env,
173 (*env)->CallObjectMethod(env, argument, argumentMethods.argumentGetValueMethod));
174 }
175
176 // If any floats or doubles add them to the allocated space
177 cArgs.floatingPointValuesCount = 0;
178 if (totalFloatingPoints > 0) {
179 // Loop through the FloatArgument list
180 for (int i = 0; i < floatCount; i++, cArgs.floatingPointValuesCount++) {
181 jobject floatArgument = (*env)->CallObjectMethod(env, jFloatsList, argumentMethods.listGetMethod, i);
182 jobject name = (*env)->CallObjectMethod(env, floatArgument, argumentMethods.getFloatNameMethod);
183 jfloat value = (*env)->CallFloatMethod(env, floatArgument, argumentMethods.getFloatValueMethod);
184
185 // Add key and value to Arguments.
189 }
190
191 for (int i = 0; i < doubleCount; i++, cArgs.floatingPointValuesCount++) {
192 jobject doubleArgument = (*env)->CallObjectMethod(env, jDoublesList, argumentMethods.listGetMethod, i);
193 jobject name = (*env)->CallObjectMethod(env, doubleArgument, argumentMethods.getDoubleNameMethod);
194 jdouble value = (*env)->CallDoubleMethod(env, doubleArgument, argumentMethods.getDoubleValueMethod);
195
196 // Add key and value to Arguments.
200 }
201 }
202
203 // Return arguments
204 return cArgs;
205}
206
207/**
208 * Get java classes that will be used to process the java {@link edu.stanford.slac.aida.lib.model.AidaArgument} class in C
209 *
210 @param env environment.
211 * @param argumentClasses the structure to store the list of classes needed to process the java {@link edu.stanford.slac.aida.lib.model.AidaArgument} class in C
212 */
213static int getArgumentClasses(JNIEnv* env, ArgumentClasses* argumentClasses) {
214 // retrieve the java.util.List interface class
215 argumentClasses->listClass = (*env)->FindClass(env, "java/util/List");
216 if (!argumentClasses->listClass) {
217 aidaThrowNonOsException(env, AIDA_INTERNAL_EXCEPTION, "Failed to get List class");
218 return EXIT_FAILURE;
219 }
220
221 // retrieve the FloatArgument class
222 argumentClasses->floatArgumentClass = (*env)->FindClass(env, "edu/stanford/slac/aida/lib/model/FloatArgument");
223 if (!argumentClasses->floatArgumentClass) {
224 aidaThrowNonOsException(env, AIDA_INTERNAL_EXCEPTION, "Failed to get FloatArgument class");
225 return EXIT_FAILURE;
226 }
227
228 // retrieve the DoubleArgument class
229 argumentClasses->doubleArgumentClass = (*env)->FindClass(env, "edu/stanford/slac/aida/lib/model/DoubleArgument");
230 if (!argumentClasses->doubleArgumentClass) {
231 aidaThrowNonOsException(env, AIDA_INTERNAL_EXCEPTION, "Failed to get DoubleArgument class");
232 return EXIT_FAILURE;
233 }
234
235 // retrieve the AidaArguments class
236 argumentClasses->aidaArgumentsClass = (*env)->FindClass(env, "edu/stanford/slac/aida/lib/model/AidaArguments");
237 if (!argumentClasses->aidaArgumentsClass) {
238 aidaThrowNonOsException(env, AIDA_INTERNAL_EXCEPTION, "Failed to get AidaArguments class");
239 return EXIT_FAILURE;
240 }
241
242 // retrieve the AidaArgument class
243 argumentClasses->aidaArgumentClass = (*env)->FindClass(env, "edu/stanford/slac/aida/lib/model/AidaArgument");
244 if (!argumentClasses->aidaArgumentClass) {
245 aidaThrowNonOsException(env, AIDA_INTERNAL_EXCEPTION, "Failed to get AidaArgument class");
246 return EXIT_FAILURE;
247 }
248 return EXIT_SUCCESS;
249}
250
251/**
252 * Get the method IDs of the java methods that will be used to process the java {@link edu.stanford.slac.aida.lib.model.AidaArgument} class in C
253 *
254 * @param env environment.
255 * @param argumentMethods the structure to store the methods needed to process the java {@link edu.stanford.slac.aida.lib.model.AidaArgument} class in C
256 */
257static void getArgumentClassMethods(JNIEnv* env, ArgumentMethods* argumentMethods) {
258 // retrieve the size and the get methods of list
259 (argumentMethods->listSizeMethod) = (*env)
260 ->GetMethodID(env, argumentMethods->argumentClasses->listClass, "size", "()I");
261 (argumentMethods->listGetMethod) = (*env)
262 ->GetMethodID(env, argumentMethods->argumentClasses->listClass, "get", "(I)Ljava/lang/Object;");
263
264 // retrieve the getName and the getValue methods
265 (argumentMethods->argumentGetNameMethod) = (*env)
266 ->GetMethodID(env, argumentMethods->argumentClasses->aidaArgumentClass, "getName", "()Ljava/lang/String;");
267 (argumentMethods->argumentGetValueMethod) = (*env)
268 ->GetMethodID(env, argumentMethods->argumentClasses->aidaArgumentClass, "getValue", "()Ljava/lang/String;");
269
270 // retrieve the getArguments, getFloats and the getDoubles methods from AidaArguments
271 (argumentMethods->argumentsGetArgumentsMethod) = (*env)
272 ->GetMethodID(env, argumentMethods->argumentClasses->aidaArgumentsClass, "getArguments",
273 "()Ljava/util/List;");
274 (argumentMethods->argumentsGetFloatArgumentsMethod) = (*env)
275 ->GetMethodID(env, argumentMethods->argumentClasses->aidaArgumentsClass, "getFloatArguments",
276 "()Ljava/util/List;");
277 (argumentMethods->argumentsGetDoubleArgumentsMethod) = (*env)
278 ->GetMethodID(env, argumentMethods->argumentClasses->aidaArgumentsClass, "getDoubleArguments",
279 "()Ljava/util/List;");
280
281 // get float and double getters from their boxed classes
282 (argumentMethods->getFloatNameMethod) = (*env)
283 ->GetMethodID(env, argumentMethods->argumentClasses->floatArgumentClass, "getName", "()Ljava/lang/String;");
284 (argumentMethods->getFloatValueMethod) = (*env)
285 ->GetMethodID(env, argumentMethods->argumentClasses->floatArgumentClass, "getValue", "()F");
286 (argumentMethods->getDoubleNameMethod) = (*env)
287 ->GetMethodID(env, argumentMethods->argumentClasses->doubleArgumentClass, "getName",
288 "()Ljava/lang/String;");
289 (argumentMethods->getDoubleValueMethod) = (*env)
290 ->GetMethodID(env, argumentMethods->argumentClasses->doubleArgumentClass, "getValue", "()D");
291}
292
293/**
294 * Check that all the java methods that will be used to process the java {@link edu.stanford.slac.aida.lib.model.AidaArgument} class in C were obtained successfully
295 *
296 * @param env environment.
297 * @param argumentMethods the list of methods needed to process the java {@link edu.stanford.slac.aida.lib.model.AidaArgument} class in C
298 * @return EXIT_SUCCESS if they were and EXIT_FAILURE if not
299 */
300static int checkMethods(JNIEnv* env, ArgumentMethods* argumentMethods) {
301 if (!argumentMethods->listSizeMethod) {
303 "Failed to get size(String) method on List object");
304 return EXIT_FAILURE;
305 }
306 if (!argumentMethods->listGetMethod) {
307 aidaThrowNonOsException(env, AIDA_INTERNAL_EXCEPTION, "Failed to get get(int) method on List object");
308 return EXIT_FAILURE;
309 }
310 if (!argumentMethods->argumentGetNameMethod) {
312 "Failed to get getName() method on AidaArgument object");
313 return EXIT_FAILURE;
314 }
315 if (!argumentMethods->argumentGetValueMethod) {
317 "Failed to get getValue() method on AidaArgument object");
318 return EXIT_FAILURE;
319 }
320
321 if (!argumentMethods->argumentsGetArgumentsMethod) {
323 "Failed to get getArguments() method on AidaArguments object");
324 return EXIT_FAILURE;
325 }
326
327 if (!argumentMethods->argumentsGetFloatArgumentsMethod) {
329 "Failed to get getFloatArguments() method on AidaArguments object");
330 return EXIT_FAILURE;
331 }
332
333 if (!argumentMethods->argumentsGetDoubleArgumentsMethod) {
335 "Failed to get getDoubleArguments() method on AidaArguments object");
336 return EXIT_FAILURE;
337 }
338
339 if (!argumentMethods->getFloatNameMethod) {
341 "Failed to get getName() method on FloatValue object");
342 return EXIT_FAILURE;
343 }
344
345 if (!argumentMethods->getFloatValueMethod) {
347 "Failed to get getValue() method on FloatValue object");
348 return EXIT_FAILURE;
349 }
350
351 if (!argumentMethods->getDoubleNameMethod) {
353 "Failed to get getName() method on DoubleValue object");
354 return EXIT_FAILURE;
355 }
356
357 if (!argumentMethods->getDoubleValueMethod) {
359 "Failed to get getValue() method on DoubleValue object");
360 return EXIT_FAILURE;
361 }
362
363 return EXIT_SUCCESS;
364}
365
366/**
367 * Allocate space for the given C {@link Arguments} structure including space for floating point numbers
368 *
369 * @param env the environment
370 * @param cArgs the C arguments structure
371 * @param totalFloatingPoints the total number of floating points contained in the arguments structure
372 * @return EXIT_SUCCESS if allocated successfully EXIT_FAILURE if not
373 */
374static int allocateSpaceForArguments(JNIEnv* env, Arguments* cArgs, int totalFloatingPoints) {
375 // Create array of arguments
376 cArgs->arguments = calloc(cArgs->argumentCount, sizeof(Argument));
377 if (!cArgs->arguments) {
379 "Failed to allocate memory for arguments");
380 return EXIT_FAILURE;
381 }
382
383 // Create space for floating point numbers
384 if (totalFloatingPoints > 0) {
385 cArgs->floatingPointValues = calloc(totalFloatingPoints, sizeof(FloatingPointValue));
386 if (!cArgs->floatingPointValues) {
387 free(cArgs->arguments);
388 cArgs->arguments = NULL;
390 "Failed to allocate memory for floating point values");
391 return EXIT_FAILURE;
392 }
393 }
394
395 return EXIT_SUCCESS;
396}
397
398/**
399 * Get value from the `VALUE` request argument,
400 * in the provided {@link Arguments} structure,
401 * when the value is a scalar.
402 *
403 * @param env environment.
404 * @param arguments provided arguments structure
405 * @return the extracted value
406 */
407Value getValue(JNIEnv* env, Arguments arguments) {
408 return getNamedValue(env, arguments, "Value");
409}
410
411/**
412 * Get value from the `VALUE` argument,
413 * in the provided {@link Arguments} structure,
414 * when the value is a scalar array.
415 *
416 * Even if the argument is not
417 * an array it will create a one element array to put it in.
418 *
419 * @param env environment.
420 * @param arguments provided arguments structure
421 * @return the extracted value
422 */
423Value getArrayValue(JNIEnv* env, Arguments arguments) {
424 return getNamedArrayValue(env, arguments, "Value");
425}
426
427/**
428 * Release all allocated memory in the given value
429 *
430 * @param value the given value'
431 */
432void releaseValue(Value value) {
433 // Only free json values because the string values will be freed with the arguments directly
434 if (value.type == AIDA_JSON_TYPE) {
435 json_value_free(value.value.jsonValue);
436 value.type = AIDA_NO_TYPE;
437 }
438}
439
440/**
441 * Create a new instance of a java boolean[],
442 * from the {@link Array} of boolean primitives.
443 *
444 * @param env environment.
445 * @param array {@link Array} of boolean primitives provided.
446 * @return new java boolean[].
447 */
448jbooleanArray toBooleanArray(JNIEnv* env, Array array) {
449 jbooleanArray returnValue;
450
451 returnValue = (*env)->NewBooleanArray(env, array.count);
452 if (!returnValue) {
453 char errorString[BUFSIZ];
454 sprintf(errorString, "Failed to create a new Boolean Array with %d elements", array.count);
456 return NULL;
457 }
458
459 // Copy values
460 (*env)->SetBooleanArrayRegion(env, returnValue, 0, array.count, array.items);
461
462 // Free up array
463 releaseArray(array);
464 return returnValue;
465}
466
467/**
468 * Create a new instance of a java byte[],
469 * from the {@link Array} of byte primitives.
470 *
471 * @param env environment.
472 * @param array {@link Array} of byte primitives provided.
473 * @return new java byte[].
474 */
475jbyteArray toByteArray(JNIEnv* env, Array array) {
476 jbyteArray returnValue;
477
478 returnValue = (*env)->NewByteArray(env, array.count);
479 if (!returnValue) {
480 char errorString[BUFSIZ];
481 sprintf(errorString, "Failed to create a new Byte Array with %d elements", array.count);
483 return NULL;
484 }
485
486 // Copy values
487 (*env)->SetByteArrayRegion(env, returnValue, 0, array.count, array.items);
488
489 // Free up array
490 releaseArray(array);
491
492 return returnValue;
493}
494
495/**
496 * Create a new instance of a java short[],
497 * from the {@link Array} of short primitives.
498 *
499 * @param env environment.
500 * @param array {@link Array} of short primitives provided.
501 * @return new java short[].
502 */
503jshortArray toShortArray(JNIEnv* env, Array array) {
504 jshortArray returnValue;
505
506 returnValue = (*env)->NewShortArray(env, array.count);
507 if (!returnValue) {
508 char errorString[BUFSIZ];
509 sprintf(errorString, "Failed to create a new Short Array with %d elements", array.count);
511 return NULL;
512 }
513
514 // Copy values to be safe about length of short copy all values
515 jshort jShortArray[array.count];
516 for (int i = 0; i < array.count; i++) {
517 jShortArray[i] = (jshort)((short*)array.items)[i];
518 }
519 (*env)->SetShortArrayRegion(env, returnValue, 0, array.count, jShortArray);
520
521 // Free up array
522 releaseArray(array);
523
524 return returnValue;
525}
526
527/**
528 * Create a new instance of a java int[],
529 * from the {@link Array} of integer primitives.
530 *
531 * @param env environment.
532 * @param array {@link Array} of integer primitives provided.
533 * @return new java int[].
534 */
535jintArray toIntegerArray(JNIEnv* env, Array array) {
536 jintArray returnValue;
537
538 returnValue = (*env)->NewIntArray(env, array.count);
539 if (!returnValue) {
540 char errorString[BUFSIZ];
541 sprintf(errorString, "Failed to create a new Integer Array with %d elements", array.count);
543 return NULL;
544 }
545
546 // Copy values. To be safe about lengths of integers copy all values
547 jint jintArray[array.count];
548 for (int i = 0; i < array.count; i++) {
549 jintArray[i] = (jint)((int*)array.items)[i];
550 }
551 (*env)->SetIntArrayRegion(env, returnValue, 0, array.count, jintArray);
552
553 // Free up array
554 releaseArray(array);
555
556 return returnValue;
557}
558
559/**
560 * Create a new instance of a java long[],
561 * from the {@link Array} of long primitives.
562 *
563 * @param env environment.
564 * @param array {@link Array} of long primitives provided.
565 * @return new java long[].
566 */
567jlongArray toLongArray(JNIEnv* env, Array array) {
568 jlongArray returnValue;
569
570 returnValue = (*env)->NewLongArray(env, array.count);
571 if (!returnValue) {
572 char errorString[BUFSIZ];
573 sprintf(errorString, "Failed to create a new Long Array with %d elements", array.count);
575 return NULL;
576 }
577
578 // Copy values. Due to the different size of longs on different sides of jni divide we need to
579 // copy each value and convert it before returning
580 jlong jlongArray[array.count];
581 for (int i = 0; i < array.count; i++) {
582 jlongArray[i] = (jlong)((long*)array.items)[i];
583 }
584 (*env)->SetLongArrayRegion(env, returnValue, 0, array.count, jlongArray);
585
586 // Free up array
587 releaseArray(array);
588
589 return returnValue;
590}
591
592/**
593 * Create a new instance of a java float[],
594 * from the {@link Array} of float primitives.
595 *
596 * @param env environment.
597 * @param array {@link Array} of float primitives provided.
598 * @return new java float[].
599 */
600jfloatArray toFloatArray(JNIEnv* env, Array array) {
601 jfloatArray returnValue;
602
603 returnValue = (*env)->NewFloatArray(env, array.count);
604 if (!returnValue) {
605 char errorString[BUFSIZ];
606 sprintf(errorString, "Failed to create a new Float Array with %d elements", array.count);
608 return NULL;
609 }
610
611 // Copy values
612 (*env)->SetFloatArrayRegion(env, returnValue, 0, array.count, array.items);
613
614 // Free up array
615 releaseArray(array);
616
617 return returnValue;
618}
619
620/**
621 * Create a new instance of a java double[],
622 * from the {@link Array} of double primitives.
623 *
624 * @param env environment.
625 * @param array {@link Array} of double primitives provided.
626 * @return new java double[].
627 */
628jdoubleArray toDoubleArray(JNIEnv* env, Array array) {
629 jdoubleArray returnValue;
630
631 // create result array
632 returnValue = (*env)->NewDoubleArray(env, array.count);
633 if (!returnValue) {
634 char errorString[BUFSIZ];
635 sprintf(errorString, "Failed to create a new Double Array with %d elements", array.count);
637 return NULL;
638 }
639
640 // Copy values
641 (*env)->SetDoubleArrayRegion(env, returnValue, 0, array.count, array.items);
642
643 // Free up array
644 releaseArray(array);
645
646 return returnValue;
647}
648
649/**
650 * Create a new instance of a java String[],
651 * from the {@link StringArray} of C string primitives.
652 *
653 * @param env environment.
654 * @param array {@link StringArray} of C string primitives provided
655 * @return new java String[]
656 */
657jobjectArray toStringArray(JNIEnv* env, StringArray array) {
658 jobjectArray returnValue;
659
660 // Get a class reference for java.lang.String
661 jclass classString = (*env)->FindClass(env, "java/lang/String");
662 if (!classString) {
663 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to get jclass of java String");
664 return NULL;
665 }
666
667 returnValue = (*env)->NewObjectArray(env, array.count, classString, NULL);
668 if (!returnValue) {
669 releaseStringArray(array);
670 char errorString[BUFSIZ];
671 sprintf(errorString, "Failed to create a new String Array with %d elements", array.count);
673 return NULL;
674 }
675
676 // Copy values
677 for (int i = 0; i < array.count; i++) {
678 (*env)->SetObjectArrayElement(env, returnValue, i, toJString(env, array.items[i]));
679 }
680
681 // Free up array
682 releaseStringArray(array);
683
684 return returnValue;
685}
686
687/**
688 * Create a new instance of a java List of Lists,
689 * from the given {@link Table} structure.
690 *
691 * Tables are returned as lists of Lists so:-
692 * we create a java ArrayList
693 * then for each column in the table
694 * we create a sub list
695 * then we loop over each row to call add() to add entries to that list
696 * then we add() the sublist to the main list
697 *
698 * @param env environment.
699 * @param table the {@link Table} provided
700 * @return new java List of Lists
701 */
702jobject toTable(JNIEnv* env, Table table) {
703 jobject tableToReturn;
704
705 JavaObject listObject = newObject(env, "edu/stanford/slac/aida/lib/model/AidaTable");
707
708 tableToReturn = listObject.object;
709 jclass cList = listObject.class;
710
711 // retrieve the add method of the list
712 jmethodID mAdd = (*env)->GetMethodID(env, cList, "add", "(ILjava/lang/Object;)Z");
713 if (!mAdd) {
715 "Failed to find the add(int, Object) method on AidaTable object");
716 return NULL;
717 }
718
719 // retrieve the add method of the list
720 jmethodID mAddField = (*env)->GetMethodID(env, cList, "addField", "(Ljava/lang/String;)Z");
721 if (!mAddField) {
723 "Failed to find the addField(String) method on AidaTable object");
724 return NULL;
725 }
726
727 // retrieve the add method of the list
728 jmethodID mAddLabel = (*env)->GetMethodID(env, cList, "addLabel", "(Ljava/lang/String;)Z");
729 if (!mAddLabel) {
731 "Failed to find the addLabel(String) method on AidaTable object");
732 return NULL;
733 }
734
735 if (!tableToReturn) {
736 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to create a new AidaTable object");
737 return NULL;
738 }
739
740 // Loop over each column
741 for (int column = 0; column < table._currentColumn; column++) {
742 // If field overrides are defined then add them
743 if (table.ppFields) {
744 char* fieldName;
745 if (column >= table._currentField || !(fieldName = table.ppFields[column])) {
747 "Fields overriden but, provider has not supplied enough field names for all table columns");
748 return NULL;
749
750 }
751 jstring stringValue = toJString(env, fieldName);
752 (*env)->CallBooleanMethod(env, tableToReturn, mAddField, stringValue);
753 (*env)->DeleteLocalRef(env, stringValue);
754 // Free up string buffer
755 free(fieldName);
756 table.ppFields[column] = NULL;
757 }
758
759 // If label overrides are defined then add them
760 if (table.ppLabels) {
761 char* labelName;
762 if (column >= table._currentLabel || !(labelName = table.ppLabels[column])) {
764 "Labels overriden but, provider has not supplied enough label names for all table columns");
765 return NULL;
766
767 }
768 jstring stringValue = toJString(env, labelName);
769 (*env)->CallBooleanMethod(env, tableToReturn, mAddLabel, stringValue);
770 (*env)->DeleteLocalRef(env, stringValue);
771 // Free up string buffer
772 free(labelName);
773 table.ppLabels[column] = NULL;
774 }
775
776 // loop over each row
777 for (int row = 0; row < table.rowCount; row++) {
778 switch (table.types[column]) {
780 jboolean data = ((jboolean*)(table.ppData[column]))[row];
781 jobject dataObject = toBoolean(env, data);
783
784 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, dataObject);
785 (*env)->DeleteLocalRef(env, dataObject);
786 break;
787 }
789 jbyte data = ((jbyte*)(table.ppData[column]))[row];
790 jobject dataObject = toByte(env, data);
792
793 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, dataObject);
794 (*env)->DeleteLocalRef(env, dataObject);
795 break;
796 }
798 jshort data = (jshort)((short*)(table.ppData[column]))[row];
799 jobject dataObject = toShort(env, data);
801
802 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, dataObject);
803 (*env)->DeleteLocalRef(env, dataObject);
804 break;
805 }
807 jint data = (jint)((int*)(table.ppData[column]))[row];
808 jobject dataObject = toInteger(env, data);
810
811 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, dataObject);
812 (*env)->DeleteLocalRef(env, dataObject);
813 break;
814 }
816 jlong data = (jlong)((long*)(table.ppData[column]))[row];
817 jobject dataObject = toLong(env, data);
819
820 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, dataObject);
821 (*env)->DeleteLocalRef(env, dataObject);
822 break;
823 }
825 jfloat data = ((jfloat*)(table.ppData[column]))[row];
826 jobject dataObject = toFloat(env, data);
828
829 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, dataObject);
830 (*env)->DeleteLocalRef(env, dataObject);
831 break;
832 }
834 jdouble data = ((jdouble*)(table.ppData[column]))[row];
835 jobject dataObject = toDouble(env, data);
837
838 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, dataObject);
839 (*env)->DeleteLocalRef(env, dataObject);
840 break;
841 }
843 char* string = ((char**)(table.ppData[column]))[row];
844 jstring stringValue = toJString(env, string);
846
847 (*env)->CallBooleanMethod(env, tableToReturn, mAdd, column, stringValue);
848
849 // Free up string buffer
850 (*env)->DeleteLocalRef(env, stringValue);
851 free(string);
852 break;
853 }
854 default:
856 "Unsupported type found in table. Perhaps you declared a table with n columns but didnt add n columns");
857 return NULL;
858 }
859 }
860 }
861
862 releaseTable(table);
863
864 return tableToReturn;
865}
866
867/**
868 * Free up any memory allocated for the given pv and arguments
869 *
870 * @param arguments the given arguments
871 */
872void releasePvAndArguments(JNIEnv* env, jstring uri, const char* pv, Arguments arguments) {
873 if (pv) {
874 (*env)->ReleaseStringUTFChars(env, uri, pv);
875 }
876 if (arguments.floatingPointValues && arguments.floatingPointValuesCount) {
877 free(arguments.floatingPointValues);
878 arguments.floatingPointValuesCount = 0;
879 arguments.floatingPointValues = NULL;
880 }
881 if (arguments.argumentCount && arguments.arguments) {
882 free(arguments.arguments);
883 arguments.argumentCount = 0;
884 arguments.arguments = NULL;
885 }
886}
887
888/**
889 * Free up any memory allocated the given scalar array
890 *
891 * @param array the given scalar array
892 */
893void releaseArray(Array array) {
894 if (array.count && array.items) {
895 free(array.items);
896 array.count = 0;
897 array.items = NULL;
898 }
899}
900
901/**
902 * Free up any memory allocated for string arrays
903 * @param array
904 */
906 if (array.count && array.items) {
907 free(array.items);
908 array.count = 0;
909 array.items = NULL;
910 }
911}
912
913/**
914 * Free up any memory allocated for the given table
915 *
916 * @param table the given tables
917 */
918void releaseTable(Table table) {
919 if (table.columnCount) {
920 if (table.ppData) {
921 for (int column = 0; column < table._currentColumn; column++) {
922 if (table.ppData[column]) {
923 free(table.ppData[column]);
924 table.ppData[column] = NULL;
925 }
926 }
927 free(table.ppData);
928 table.ppData = NULL;
929
930 if (table.types) {
931 free(table.types);
932 table.types = NULL;
933 }
934 table._currentColumn = 0;
935 }
936
937 // Free field names
938 if (table.ppFields) {
939 for (int column = 0; column < table._currentField; column++) {
940 if (table.ppFields[column]) {
941 free(table.ppFields[column]);
942 table.ppFields[column] = NULL;
943 }
944 }
945 free(table.ppFields);
946 table._currentField = 0;
947 table.ppFields = NULL;
948 }
949
950 // Free label names
951 if (table.ppLabels) {
952 for (int column = 0; column < table._currentLabel; column++) {
953 if (table.ppLabels[column]) {
954 free(table.ppLabels[column]);
955 table.ppLabels[column] = NULL;
956 }
957 }
958 free(table.ppLabels);
959 table._currentLabel = 0;
960 table.ppLabels = NULL;
961 }
962
963 table.columnCount = 0;
964 }
965}
966
967/**
968 * Get the class and the valueOf() method for the given signature
969 *
970 * @param env
971 * @param boxedClassSignature
972 * @param valueOfMethodSignature
973 * @return
974 */
975ClassAndMethod getClassAndValueOfMethod(JNIEnv* env, char* boxedClassSignature, char* valueOfMethodSignature) {
976 ClassAndMethod classAndMethod;
977
978 // Get a class reference
979 classAndMethod.class = (*env)->FindClass(env, boxedClassSignature);
980 if (!classAndMethod.class) {
981 char errorString[BUFSIZ];
982 sprintf(errorString, "Failed to get class of: %s", boxedClassSignature);
984 return classAndMethod;
985 }
986
987 // retrieve the valueOf method
988 classAndMethod.methodId = (*env)->GetStaticMethodID(env, classAndMethod.class, "valueOf", valueOfMethodSignature);
989 if (!classAndMethod.methodId) {
990 char errorString[BUFSIZ];
991 sprintf(errorString, "Failed to valueOf method with signature: %s", valueOfMethodSignature);
993 return classAndMethod;
994 }
995
996 return classAndMethod;
997}
998
999/**
1000 * Create a new instance of a java Boolean,
1001 * from the primitive boolean provided.
1002 *
1003 * @param env environment.
1004 * @param primitive primitive boolean provided
1005 * @return new java Boolean
1006 */
1007jobject toBoolean(JNIEnv* env, jboolean primitive) {
1008 ClassAndMethod classAndMethod = getClassAndValueOfMethod(env, "java/lang/Boolean", "(Z)Ljava/lang/Boolean;");
1010
1011 jobject dataObject = (*env)->CallStaticObjectMethod(env, classAndMethod.class, classAndMethod.methodId, primitive);
1012 if (!dataObject) {
1013 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to convert boolean to Boolean");
1014 }
1015
1016 return dataObject;
1017}
1018
1019/**
1020 * Create a new instance of a java Byte,
1021 * from the primitive byte provided.
1022 *
1023 * @param env environment.
1024 * @param primitive primitive byte provided
1025 * @return new java Byte
1026 */
1027jobject toByte(JNIEnv* env, jbyte primitive) {
1028 ClassAndMethod classAndMethod = getClassAndValueOfMethod(env, "java/lang/Byte", "(B)Ljava/lang/Byte;");
1030
1031 jobject dataObject = (*env)->CallStaticObjectMethod(env, classAndMethod.class, classAndMethod.methodId, primitive);
1032 if (!dataObject) {
1033 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to convert byte to Byte");
1034 }
1035
1036 return dataObject;
1037}
1038
1039/**
1040 * Create a new instance of a java Short,
1041 * from the primitive short provided.
1042 *
1043 * @param env environment.
1044 * @param primitive primitive short provided
1045 * @return new java Short
1046 */
1047jobject toShort(JNIEnv* env, jshort primitive) {
1048 ClassAndMethod classAndMethod = getClassAndValueOfMethod(env, "java/lang/Short", "(S)Ljava/lang/Short;");
1050
1051 jobject dataObject = (*env)->CallStaticObjectMethod(env, classAndMethod.class, classAndMethod.methodId, primitive);
1052 if (!dataObject) {
1053 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to convert short to Short");
1054 }
1055
1056 return dataObject;
1057}
1058
1059/**
1060 * Create a new instance of a java Integer,
1061 * from the primitive int provided.
1062 *
1063 * @param env environment.
1064 * @param primitive primitive int provided
1065 * @return new java Integer
1066 */
1067jobject toInteger(JNIEnv* env, jint primitive) {
1068 ClassAndMethod classAndMethod = getClassAndValueOfMethod(env, "java/lang/Integer", "(I)Ljava/lang/Integer;");
1070
1071 jobject dataObject = (*env)->CallStaticObjectMethod(env, classAndMethod.class, classAndMethod.methodId, primitive);
1072 if (!dataObject) {
1073 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to convert int to Integer");
1074 }
1075
1076 return dataObject;
1077}
1078
1079/**
1080 * Create a new instance of a java Long,
1081 * from the primitive long provided.
1082 *
1083 * @param env environment.
1084 * @param primitive primitive long provided
1085 * @return new java Long
1086 */
1087jobject toLong(JNIEnv* env, jlong primitive) {
1088 ClassAndMethod classAndMethod = getClassAndValueOfMethod(env, "java/lang/Long", "(J)Ljava/lang/Long;");
1090
1091 jobject dataObject = (*env)->CallStaticObjectMethod(env, classAndMethod.class, classAndMethod.methodId, primitive);
1092 if (!dataObject) {
1093 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to convert long to Long");
1094 }
1095
1096 return dataObject;
1097}
1098
1099/**
1100 * Create a new instance of a java Float,
1101 * from the primitive float provided.
1102 *
1103 * @param env environment.
1104 * @param primitive primitive float provided
1105 * @return new java Float
1106 */
1107jobject toFloat(JNIEnv* env, jfloat primitive) {
1108 ClassAndMethod classAndMethod = getClassAndValueOfMethod(env, "java/lang/Float", "(F)Ljava/lang/Float;");
1110
1111 jobject dataObject = (*env)->CallStaticObjectMethod(env, classAndMethod.class, classAndMethod.methodId, primitive);
1112 if (!dataObject) {
1113 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to convert float to Float");
1114 }
1115
1116 return dataObject;
1117}
1118
1119/**
1120 * Create a new instance of a java Double,
1121 * from the primitive double provided.
1122 *
1123 * @param env environment.
1124 * @param primitive primitive double provided
1125 * @return new java Double
1126 */
1127jobject toDouble(JNIEnv* env, jdouble primitive) {
1128 ClassAndMethod classAndMethod = getClassAndValueOfMethod(env, "java/lang/Double", "(D)Ljava/lang/Double;");
1130
1131 jobject dataObject = (*env)->CallStaticObjectMethod(env, classAndMethod.class, classAndMethod.methodId, primitive);
1132 if (!dataObject) {
1133 aidaThrowNonOsException(env, UNABLE_TO_GET_DATA_EXCEPTION, "Failed to convert double to Double");
1134 }
1135
1136 return dataObject;
1137}
1138
1139
#define ON_EXCEPTION_FREE_STRING_AND_RETURN_(_r)
Check to see if an exception has been raised, free a local variable string, and return the given retu...
#define ON_EXCEPTION_RETURN_(_r)
Check to see if an exception has been raised, and return the given return value.
#define AIDA_INTERNAL_EXCEPTION
Use this string to signal Internal Exceptions in aidaThrow()
#define UNABLE_TO_GET_DATA_EXCEPTION
Use this string to signal Exceptions when trying to Get Data in aidaThrow()
Value getArrayValue(JNIEnv *env, Arguments arguments)
Get value from the VALUE argument, in the provided Arguments structure, when the value is a scalar ar...
jlongArray toLongArray(JNIEnv *env, Array array)
Create a new instance of a java long[], from the Array of long primitives.
jobject toTable(JNIEnv *env, Table table)
Create a new instance of a java List of Lists, from the given Table structure.
jobject newObjectFromClass(JNIEnv *env, jclass class)
Create a new java object.
jbooleanArray toBooleanArray(JNIEnv *env, Array array)
Create a new instance of a java boolean[], from the Array of boolean primitives.
void releaseArray(Array array)
Free up any memory allocated the given scalar array.
jmethodID getConstructorMethodId(JNIEnv *env, jclass clazz)
Get the method ID of the constructor of the given class.
jobject toInteger(JNIEnv *env, jint primitive)
Create a new instance of a java Integer, from the primitive int provided.
jshortArray toShortArray(JNIEnv *env, Array array)
Create a new instance of a java short[], from the Array of short primitives.
JavaObject newObject(JNIEnv *env, char *clazz)
Look up class in the given env, and create a new java object.
jobject toLong(JNIEnv *env, jlong primitive)
Create a new instance of a java Long, from the primitive long provided.
jobjectArray toStringArray(JNIEnv *env, StringArray array)
Create a new instance of a java String[], from the StringArray of C string primitives.
jfloatArray toFloatArray(JNIEnv *env, Array array)
Create a new instance of a java float[], from the Array of float primitives.
jobject toFloat(JNIEnv *env, jfloat primitive)
Create a new instance of a java Float, from the primitive float provided.
jobject toBoolean(JNIEnv *env, jboolean primitive)
Create a new instance of a java Boolean, from the primitive boolean provided.
jobject toShort(JNIEnv *env, jshort primitive)
Create a new instance of a java Short, from the primitive short provided.
Arguments toArguments(JNIEnv *env, jobject jArguments)
Get an Arguments structure, from the given java List<AidaArgument>
jintArray toIntegerArray(JNIEnv *env, Array array)
Create a new instance of a java int[], from the Array of integer primitives.
jobject toDouble(JNIEnv *env, jdouble primitive)
Create a new instance of a java Double, from the primitive double provided.
void releaseValue(Value value)
Release all allocated memory in the given value.
jbyteArray toByteArray(JNIEnv *env, Array array)
Create a new instance of a java byte[], from the Array of byte primitives.
jmethodID getMethodId(JNIEnv *env, jclass clazz, char *methodName, char *methodSignature)
Get the method ID on the given class, with the given method name, and signature.
void releaseTable(Table table)
Free up any memory allocated for the given table.
ClassAndMethod getClassAndValueOfMethod(JNIEnv *env, char *boxedClassSignature, char *valueOfMethodSignature)
Get the class and the valueOf() method for the given signature.
void releaseStringArray(StringArray array)
Free up any memory allocated for string arrays.
Value getValue(JNIEnv *env, Arguments arguments)
Get value from the VALUE request argument, in the provided Arguments structure, when the value is a s...
jdoubleArray toDoubleArray(JNIEnv *env, Array array)
Create a new instance of a java double[], from the Array of double primitives.
jobject toByte(JNIEnv *env, jbyte primitive)
Create a new instance of a java Byte, from the primitive byte provided.
jstring toJString(JNIEnv *env, const char *string)
Convert C string to jstring.
void releasePvAndArguments(JNIEnv *env, jstring uri, const char *pv, Arguments arguments)
Free up any memory allocated for the given pv and arguments.
char * toCString(JNIEnv *env, jstring string)
Convert given jstring to a C string.
The Header File for the JNI helper functions.
Value getNamedArrayValue(JNIEnv *env, Arguments arguments, char *name)
Get value from a named argument in the provided arguments structure.
Value getNamedValue(JNIEnv *env, Arguments arguments, char *name)
Get value from a named argument in the provided arguments structure.
void aidaThrowNonOsException(JNIEnv *env, char *exception, const char *message)
To log any non-OS exceptions and throw back to java.
The Header File for the Native Channel Provider Server Helper functions.
@ AIDA_SHORT_ARRAY_TYPE
Represents a short array.
@ AIDA_FLOAT_ARRAY_TYPE
Represents a float array.
@ AIDA_BOOLEAN_ARRAY_TYPE
Represents a boolean array.
@ AIDA_INTEGER_ARRAY_TYPE
Represents an integer array.
@ AIDA_JSON_TYPE
Argument was provided as JSON text.
@ AIDA_BYTE_ARRAY_TYPE
Represents a byte array.
@ AIDA_LONG_ARRAY_TYPE
Represents a long array.
@ AIDA_STRING_ARRAY_TYPE
Represents a string array.
@ AIDA_NO_TYPE
Used to indicate that no type was provided as an argument.
@ AIDA_DOUBLE_ARRAY_TYPE
Represents a double array.
A single request argument.
char * value
The string value of the argument.
char * name
The name of the argument.
An Arguments structure stores all of the arguments passed from the request to the Native Channel Prov...
int argumentCount
The number of arguments sent with this request.
int floatingPointValuesCount
The number of floating point numbers in the arguments of this request.
Argument * arguments
The array of Arguments.
FloatingPointValue * floatingPointValues
The array of FloatingPointValue.
An array of data.
void * items
The items in this array.
int count
The number of items in this array.
Represents a floating point number.
char * path
The string path from the root of the json structure in which this value is found.
bool isFloat
Determines whether the value is a single or double precision floating point value.
FloatOrDoubleValue value
The floating point value.
An array of string data.
int count
The number of items in this array.
char ** items
The items in this array - pointers to the strings you allocate.
Table structure.
int columnCount
number of columns in table
char ** ppLabels
the overridden label names. if null, not overridden. If not null then array of pointers to allocated ...
int _currentLabel
For internal use by addLabel() etc.
char ** ppFields
the overridden field names. if null, not overridden. If not null then array of pointers to allocated ...
Type * types
the scalar type of each column, one of BOOLEAN, BYTE, SHORT, INTEGER, LONG, FLOAT,...
void ** ppData
the data. an array of [rows][columns]
int _currentField
For internal use by addField() etc.
int rowCount
number of rows in table
int _currentColumn
For internal use by addColumn() etc.
This special type represents a Value.
ValueContents value
The value's contents, either a string or parsed json.
Type type
AIDA_STRING_TYPE or AIDA_JSON_TYPE.
double doubleValue
The double precision floating point value.
float floatValue
The single precision floating point value.
json_value * jsonValue
The parsed json_value of this Value if the type is AIDA_JSON_TYPE.