LCOV - code coverage report
Current view: top level - src/plugins/jni - jni.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 23 181 12.7 %
Date: 2019-09-12 12:28:41 Functions: 4 10 40.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #ifndef HAVE_KDBCONFIG
      10             : #include "kdbconfig.h"
      11             : #endif
      12             : 
      13             : #include <string.h>
      14             : 
      15             : #include <jni.h>
      16             : #include <stdlib.h>
      17             : 
      18             : 
      19             : #include <kdberrors.h>
      20             : #include <kdbplugin.h>
      21             : 
      22             : // forward declarations
      23             : int elektraJniOpen (Plugin * handle, Key * errorKey);
      24             : int elektraJniClose (Plugin * handle, Key * errorKey);
      25             : int elektraJniGet (Plugin * handle, KeySet * ks, Key * parentKey);
      26             : int elektraJniSet (Plugin * handle, KeySet * ks, Key * parentKey);
      27             : int elektraJniError (Plugin * handle, KeySet * ks, Key * parentKey);
      28             : 
      29             : // plugin's data handle
      30             : typedef struct
      31             : {
      32             :         JNIEnv * env;
      33             :         JavaVM * jvm;
      34             :         jclass clsPlugin;
      35             :         jclass clsKey;
      36             :         jclass clsKeySet;
      37             :         int module;
      38             :         int printException;
      39             :         jmethodID midKeyConstr;
      40             :         jmethodID midKeySetConstr;
      41             :         jmethodID midKeyRelease;
      42             :         jmethodID midKeySetRelease;
      43             :         jobject plugin;
      44             : } Data;
      45             : 
      46           0 : static void checkException (Data * data, const char * when, Key * warningKey)
      47             : {
      48           0 :         if ((*data->env)->ExceptionCheck (data->env))
      49             :         {
      50           0 :                 if (data->printException)
      51             :                 {
      52           0 :                         (*data->env)->ExceptionDescribe (data->env);
      53             :                 }
      54           0 :                 jthrowable ex = (*data->env)->ExceptionOccurred (data->env);
      55           0 :                 jmethodID toString = (*data->env)
      56           0 :                                              ->GetMethodID (data->env, (*data->env)->FindClass (data->env, "java/lang/Object"), "toString",
      57             :                                                             "()Ljava/lang/String;");
      58           0 :                 jstring estr = (jstring) (*data->env)->CallObjectMethod (data->env, ex, toString);
      59             : 
      60           0 :                 jboolean iseCopy = JNI_FALSE;
      61           0 :                 const char * which = "unknown";
      62           0 :                 if (estr)
      63             :                 {
      64           0 :                         which = (*data->env)->GetStringUTFChars (data->env, estr, &iseCopy);
      65             :                 }
      66             : 
      67           0 :                 ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNINGF (warningKey, "During \"%s\", java exception was thrown: %s", when, which);
      68             : 
      69           0 :                 if (iseCopy == JNI_TRUE)
      70             :                 {
      71           0 :                         (*data->env)->ReleaseStringUTFChars (data->env, estr, which);
      72             :                 }
      73           0 :                 (*data->env)->ExceptionClear (data->env);
      74             :         }
      75           0 : }
      76             : 
      77           0 : static int call1Arg (Data * data, Key * errorKey, const char * method)
      78             : {
      79           0 :         jobject jerrorKey = (*data->env)->NewObject (data->env, data->clsKey, data->midKeyConstr, errorKey);
      80           0 :         checkException (data, method, errorKey);
      81           0 :         if (jerrorKey == 0)
      82             :         {
      83           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Cannot create errorKey in %s", method);
      84           0 :                 return -1;
      85             :         }
      86             : 
      87           0 :         jmethodID mid = (*data->env)->GetMethodID (data->env, data->clsPlugin, method, "(Lorg/libelektra/Key;)I");
      88           0 :         checkException (data, method, errorKey);
      89           0 :         if (mid == 0)
      90             :         {
      91           0 :                 ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Cannot find org/libelektra/Key in %s", method);
      92           0 :                 return -1;
      93             :         }
      94             : 
      95           0 :         jint result = 0;
      96           0 :         result = (*data->env)->CallIntMethod (data->env, data->plugin, mid, jerrorKey);
      97           0 :         if ((*data->env)->ExceptionCheck (data->env))
      98             :         {
      99           0 :                 ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "Method '%s' failed with exception", method);
     100           0 :                 result = -1;
     101             :         }
     102           0 :         checkException (data, method, errorKey);
     103             : 
     104           0 :         (*data->env)->CallVoidMethod (data->env, jerrorKey, data->midKeyRelease);
     105           0 :         checkException (data, method, errorKey);
     106             : 
     107           0 :         return result;
     108             : }
     109             : 
     110           0 : static int call2Arg (Data * data, KeySet * ks, Key * errorKey, const char * method)
     111             : {
     112           0 :         jobject jks = (*data->env)->NewObject (data->env, data->clsKeySet, data->midKeySetConstr, ks);
     113           0 :         checkException (data, method, errorKey);
     114           0 :         if (jks == 0)
     115             :         {
     116           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot create ks");
     117           0 :                 return -1;
     118             :         }
     119             : 
     120           0 :         jobject jkey = (*data->env)->NewObject (data->env, data->clsKey, data->midKeyConstr, errorKey);
     121           0 :         checkException (data, method, errorKey);
     122           0 :         if (jkey == 0)
     123             :         {
     124           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot create key");
     125           0 :                 return -1;
     126             :         }
     127             : 
     128           0 :         jmethodID mid = (*data->env)->GetMethodID (data->env, data->clsPlugin, method, "(Lorg/libelektra/KeySet;Lorg/libelektra/Key;)I");
     129           0 :         checkException (data, method, errorKey);
     130           0 :         if (mid == 0)
     131             :         {
     132           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Cannot find %s", method);
     133           0 :                 return -1;
     134             :         }
     135             : 
     136           0 :         jint result = 0;
     137           0 :         result = (*data->env)->CallIntMethod (data->env, data->plugin, mid, jks, jkey);
     138           0 :         if ((*data->env)->ExceptionCheck (data->env))
     139             :         {
     140           0 :                 ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "Method '%s' failed with exception", method);
     141           0 :                 result = -1;
     142             :         }
     143           0 :         checkException (data, method, errorKey);
     144             : 
     145           0 :         (*data->env)->CallVoidMethod (data->env, jks, data->midKeySetRelease);
     146           0 :         checkException (data, method, errorKey);
     147             : 
     148           0 :         (*data->env)->CallVoidMethod (data->env, jkey, data->midKeyRelease);
     149           0 :         checkException (data, method, errorKey);
     150             : 
     151           0 :         return result;
     152             : }
     153             : 
     154          15 : int elektraJniOpen (Plugin * handle, Key * errorKey)
     155             : {
     156          15 :         Data * data = elektraMalloc (sizeof (Data));
     157          15 :         data->module = 0;
     158          15 :         data->printException = 0;
     159          15 :         elektraPluginSetData (handle, data);
     160             : 
     161          15 :         KeySet * config = elektraPluginGetConfig (handle);
     162          15 :         Key * k = ksLookupByName (config, "/module", 0);
     163          15 :         if (k)
     164             :         {
     165          15 :                 data->module = 1;
     166          15 :                 return 0;
     167             :         }
     168             : 
     169           0 :         k = ksLookupByName (config, "/print", 0);
     170           0 :         if (k)
     171             :         {
     172           0 :                 data->printException = 1;
     173             :         }
     174             : 
     175           0 :         k = ksLookupByName (config, "/classpath", 0);
     176           0 :         if (!k)
     177             :         {
     178           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Could not find plugin config /classpath");
     179           0 :                 return -1;
     180             :         }
     181           0 :         char classpatharg[] = "-Djava.class.path=";
     182           0 :         char * classpath = elektraMalloc (sizeof (classpatharg) + keyGetValueSize (k));
     183           0 :         strcpy (classpath, classpatharg);
     184           0 :         strcat (classpath, keyString (k));
     185             : 
     186           0 :         k = ksLookupByName (config, "/option", 0);
     187           0 :         char * option = 0;
     188           0 :         if (!k)
     189             :         {
     190             :                 option = "-verbose:gc,class,jni";
     191             :         }
     192             :         else
     193             :         {
     194           0 :                 option = (char *) keyString (k);
     195             :         }
     196             : 
     197           0 :         k = ksLookupByName (config, "/ignore", 0);
     198           0 :         jboolean ign = JNI_FALSE;
     199           0 :         if (k) ign = JNI_TRUE;
     200             : 
     201             :         /* TODO: check if JVM is already started:
     202             :         jsize nVMs;
     203             :         JNI_GetCreatedJavaVMs(NULL, 0, &nVMs); // get array length
     204             :         JavaVM** buffer = elektraMalloc(nVMs, sizeof(JavaVM*));
     205             :         JNI_GetCreatedJavaVMs(buffer, nVMs, &nVMs); // get data
     206             :         */
     207             : 
     208             :         JavaVMInitArgs vmArgs;
     209             :         JavaVMOption options[2];
     210           0 :         options[0].optionString = classpath;
     211           0 :         options[1].optionString = option;
     212           0 :         vmArgs.version = JNI_VERSION_1_8;
     213           0 :         vmArgs.nOptions = 2;
     214           0 :         vmArgs.options = options;
     215           0 :         vmArgs.ignoreUnrecognized = ign;
     216             : 
     217           0 :         jint res = JNI_CreateJavaVM (&data->jvm, (void **) &data->env, (void **) &vmArgs);
     218           0 :         elektraFree (classpath);
     219           0 :         if (res < 0)
     220             :         {
     221           0 :                 switch (res)
     222             :                 {
     223             :                 case JNI_EDETACHED:
     224           0 :                         ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Cannot create Java VM: Thread detached from the VM");
     225           0 :                         return -1;
     226             :                 case JNI_EVERSION:
     227           0 :                         ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Cannot create Java VM: JNI version error");
     228           0 :                         return -1;
     229             :                 case JNI_ENOMEM:
     230           0 :                         ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Cannot create Java VM: Not enough memory");
     231           0 :                         return -1;
     232             :                 case JNI_EEXIST:
     233           0 :                         ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot create Java VM: VM already created");
     234           0 :                         return -1;
     235             :                 case JNI_EINVAL:
     236           0 :                         ELEKTRA_SET_INTERFACE_ERROR (errorKey, "Cannot create Java VM: Invalid arguments");
     237           0 :                         return -1;
     238             :                 default:
     239           0 :                         ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Cannot create Java VM: Unknown error");
     240           0 :                         return -1;
     241             :                 }
     242             :         }
     243             : 
     244           0 :         k = ksLookupByName (config, "/classname", 0);
     245           0 :         if (!k)
     246             :         {
     247           0 :                 ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Could not find plugin config /classname");
     248           0 :                 return -1;
     249             :         }
     250             : 
     251           0 :         const char * classname = keyString (k);
     252             : 
     253           0 :         data->clsPlugin = (*data->env)->FindClass (data->env, classname);
     254           0 :         if (data->clsPlugin == 0)
     255             :         {
     256           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Cannot find class %s", classname);
     257           0 :                 return -1;
     258             :         }
     259             : 
     260           0 :         data->clsKey = (*data->env)->FindClass (data->env, "org/libelektra/Key");
     261           0 :         if (data->clsKey == 0)
     262             :         {
     263           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find class Key");
     264           0 :                 return -1;
     265             :         }
     266             : 
     267           0 :         data->clsKeySet = (*data->env)->FindClass (data->env, "org/libelektra/KeySet");
     268           0 :         if (data->clsKeySet == 0)
     269             :         {
     270           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find class KeySet");
     271           0 :                 return -1;
     272             :         }
     273             : 
     274           0 :         data->midKeyConstr = (*data->env)->GetMethodID (data->env, data->clsKey, "<init>", "(J)V");
     275           0 :         if (data->midKeyConstr == 0)
     276             :         {
     277           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find constructor of Key");
     278           0 :                 return -1;
     279             :         }
     280             : 
     281           0 :         data->midKeySetConstr = (*data->env)->GetMethodID (data->env, data->clsKeySet, "<init>", "(J)V");
     282           0 :         if (data->midKeySetConstr == 0)
     283             :         {
     284           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find constructor of KeySet");
     285           0 :                 return -1;
     286             :         }
     287             : 
     288           0 :         data->midKeyRelease = (*data->env)->GetMethodID (data->env, data->clsKey, "release", "()V");
     289           0 :         if (data->midKeyRelease == 0)
     290             :         {
     291           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find release of Key");
     292           0 :                 return -1;
     293             :         }
     294             : 
     295           0 :         data->midKeySetRelease = (*data->env)->GetMethodID (data->env, data->clsKeySet, "release", "()V");
     296           0 :         if (data->midKeySetRelease == 0)
     297             :         {
     298           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find release of KeySet");
     299           0 :                 return -1;
     300             :         }
     301             : 
     302           0 :         jmethodID midPluginConstructor = (*data->env)->GetMethodID (data->env, data->clsPlugin, "<init>", "()V");
     303           0 :         if (midPluginConstructor == 0)
     304             :         {
     305           0 :                 ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find constructor of plugin");
     306           0 :                 return -1;
     307             :         }
     308             : 
     309           0 :         data->plugin = (*data->env)->NewObject (data->env, data->clsPlugin, midPluginConstructor);
     310           0 :         checkException (data, "creating plugin", errorKey);
     311           0 :         if (data->plugin == 0)
     312             :         {
     313           0 :                 ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERROR (errorKey, "Cannot create plugin");
     314           0 :                 return -1;
     315             :         }
     316             : 
     317           0 :         return call2Arg (data, config, errorKey, "open");
     318             : }
     319             : 
     320          15 : int elektraJniClose (Plugin * handle, Key * errorKey)
     321             : {
     322          15 :         Data * data = elektraPluginGetData (handle);
     323             : 
     324          15 :         if (!data || data->module == 1)
     325             :         {
     326             :                 return 0;
     327             :         }
     328             : 
     329           0 :         int ret = call1Arg (data, errorKey, "close");
     330             : 
     331           0 :         (*data->jvm)->DestroyJavaVM (data->jvm);
     332           0 :         elektraFree (data);
     333             : 
     334           0 :         return ret;
     335             : }
     336             : 
     337          15 : int elektraJniGet (Plugin * handle, KeySet * returned, Key * parentKey)
     338             : {
     339          15 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/jni"))
     340             :         {
     341          15 :                 KeySet * contract =
     342          15 :                         ksNew (30, keyNew ("system/elektra/modules/jni", KEY_VALUE, "jni plugin waits for your orders", KEY_END),
     343             :                                keyNew ("system/elektra/modules/jni/exports", KEY_END),
     344             :                                keyNew ("system/elektra/modules/jni/exports/open", KEY_FUNC, elektraJniOpen, KEY_END),
     345             :                                keyNew ("system/elektra/modules/jni/exports/close", KEY_FUNC, elektraJniClose, KEY_END),
     346             :                                keyNew ("system/elektra/modules/jni/exports/get", KEY_FUNC, elektraJniGet, KEY_END),
     347             :                                keyNew ("system/elektra/modules/jni/exports/set", KEY_FUNC, elektraJniSet, KEY_END),
     348             :                                keyNew ("system/elektra/modules/jni/exports/error", KEY_FUNC, elektraJniError, KEY_END),
     349             : #include ELEKTRA_README
     350             :                                keyNew ("system/elektra/modules/jni/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     351          15 :                 ksAppend (returned, contract);
     352          15 :                 ksDel (contract);
     353             :         }
     354             : 
     355          15 :         Data * data = elektraPluginGetData (handle);
     356             : 
     357          15 :         if (!data || data->module == 1)
     358             :         {
     359             :                 return 0;
     360             :         }
     361             : 
     362           0 :         return call2Arg (data, returned, parentKey, "get");
     363             : }
     364             : 
     365           0 : int elektraJniSet (Plugin * handle, KeySet * returned, Key * parentKey)
     366             : {
     367           0 :         Data * data = elektraPluginGetData (handle);
     368           0 :         return call2Arg (data, returned, parentKey, "set");
     369             : }
     370             : 
     371           0 : int elektraJniError (Plugin * handle, KeySet * returned, Key * parentKey)
     372             : {
     373           0 :         Data * data = elektraPluginGetData (handle);
     374           0 :         return call2Arg (data, returned, parentKey, "error");
     375             : }
     376             : 
     377          15 : Plugin * ELEKTRA_PLUGIN_EXPORT
     378             : {
     379             :         // clang-format off
     380          15 :         return elektraPluginExport("jni",
     381             :                 ELEKTRA_PLUGIN_OPEN,    &elektraJniOpen,
     382             :                 ELEKTRA_PLUGIN_CLOSE,   &elektraJniClose,
     383             :                 ELEKTRA_PLUGIN_GET,     &elektraJniGet,
     384             :                 ELEKTRA_PLUGIN_SET,     &elektraJniSet,
     385             :                 ELEKTRA_PLUGIN_ERROR,   &elektraJniError,
     386             :                 ELEKTRA_PLUGIN_END);
     387             : }
     388             : 

Generated by: LCOV version 1.13