jni_helper.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include "config.h"
  19. #include "jni_helper.h"
  20. #include <stdio.h>
  21. #include <string.h>
  22. static pthread_mutex_t hdfsHashMutex = PTHREAD_MUTEX_INITIALIZER;
  23. static pthread_mutex_t jvmMutex = PTHREAD_MUTEX_INITIALIZER;
  24. static volatile int hashTableInited = 0;
  25. #define LOCK_HASH_TABLE() pthread_mutex_lock(&hdfsHashMutex)
  26. #define UNLOCK_HASH_TABLE() pthread_mutex_unlock(&hdfsHashMutex)
  27. /** The Native return types that methods could return */
  28. #define VOID 'V'
  29. #define JOBJECT 'L'
  30. #define JARRAYOBJECT '['
  31. #define JBOOLEAN 'Z'
  32. #define JBYTE 'B'
  33. #define JCHAR 'C'
  34. #define JSHORT 'S'
  35. #define JINT 'I'
  36. #define JLONG 'J'
  37. #define JFLOAT 'F'
  38. #define JDOUBLE 'D'
  39. /**
  40. * MAX_HASH_TABLE_ELEM: The maximum no. of entries in the hashtable.
  41. * It's set to 4096 to account for (classNames + No. of threads)
  42. */
  43. #define MAX_HASH_TABLE_ELEM 4096
  44. /** Key that allows us to retrieve thread-local storage */
  45. static pthread_key_t gTlsKey;
  46. /** nonzero if we succeeded in initializing gTlsKey. Protected by the jvmMutex */
  47. static int gTlsKeyInitialized = 0;
  48. /** Pthreads thread-local storage for each library thread. */
  49. struct hdfsTls {
  50. JNIEnv *env;
  51. };
  52. /**
  53. * The function that is called whenever a thread with libhdfs thread local data
  54. * is destroyed.
  55. *
  56. * @param v The thread-local data
  57. */
  58. static void hdfsThreadDestructor(void *v)
  59. {
  60. struct hdfsTls *tls = v;
  61. JavaVM *vm;
  62. JNIEnv *env = tls->env;
  63. jint ret;
  64. ret = (*env)->GetJavaVM(env, &vm);
  65. if (ret) {
  66. fprintf(stderr, "hdfsThreadDestructor: GetJavaVM failed with "
  67. "error %d\n", ret);
  68. (*env)->ExceptionDescribe(env);
  69. } else {
  70. (*vm)->DetachCurrentThread(vm);
  71. }
  72. free(tls);
  73. }
  74. static int validateMethodType(MethType methType)
  75. {
  76. if (methType != STATIC && methType != INSTANCE) {
  77. fprintf(stderr, "Unimplemented method type\n");
  78. return 0;
  79. }
  80. return 1;
  81. }
  82. static int hashTableInit(void)
  83. {
  84. if (!hashTableInited) {
  85. LOCK_HASH_TABLE();
  86. if (!hashTableInited) {
  87. if (hcreate(MAX_HASH_TABLE_ELEM) == 0) {
  88. fprintf(stderr, "error creating hashtable, <%d>: %s\n",
  89. errno, strerror(errno));
  90. return 0;
  91. }
  92. hashTableInited = 1;
  93. }
  94. UNLOCK_HASH_TABLE();
  95. }
  96. return 1;
  97. }
  98. static int insertEntryIntoTable(const char *key, void *data)
  99. {
  100. ENTRY e, *ep;
  101. if (key == NULL || data == NULL) {
  102. return 0;
  103. }
  104. if (! hashTableInit()) {
  105. return -1;
  106. }
  107. e.data = data;
  108. e.key = (char*)key;
  109. LOCK_HASH_TABLE();
  110. ep = hsearch(e, ENTER);
  111. UNLOCK_HASH_TABLE();
  112. if (ep == NULL) {
  113. fprintf(stderr, "warn adding key (%s) to hash table, <%d>: %s\n",
  114. key, errno, strerror(errno));
  115. }
  116. return 0;
  117. }
  118. static void* searchEntryFromTable(const char *key)
  119. {
  120. ENTRY e,*ep;
  121. if (key == NULL) {
  122. return NULL;
  123. }
  124. hashTableInit();
  125. e.key = (char*)key;
  126. LOCK_HASH_TABLE();
  127. ep = hsearch(e, FIND);
  128. UNLOCK_HASH_TABLE();
  129. if (ep != NULL) {
  130. return ep->data;
  131. }
  132. return NULL;
  133. }
  134. int invokeMethod(JNIEnv *env, RetVal *retval, Exc *exc, MethType methType,
  135. jobject instObj, const char *className,
  136. const char *methName, const char *methSignature, ...)
  137. {
  138. va_list args;
  139. jclass cls;
  140. jmethodID mid;
  141. jthrowable jthr;
  142. const char *str;
  143. char returnType;
  144. if (! validateMethodType(methType)) {
  145. return -1;
  146. }
  147. cls = globalClassReference(className, env);
  148. if (cls == NULL) {
  149. return -2;
  150. }
  151. mid = methodIdFromClass(className, methName, methSignature,
  152. methType, env);
  153. if (mid == NULL) {
  154. (*env)->ExceptionDescribe(env);
  155. return -3;
  156. }
  157. str = methSignature;
  158. while (*str != ')') str++;
  159. str++;
  160. returnType = *str;
  161. va_start(args, methSignature);
  162. if (returnType == JOBJECT || returnType == JARRAYOBJECT) {
  163. jobject jobj = NULL;
  164. if (methType == STATIC) {
  165. jobj = (*env)->CallStaticObjectMethodV(env, cls, mid, args);
  166. }
  167. else if (methType == INSTANCE) {
  168. jobj = (*env)->CallObjectMethodV(env, instObj, mid, args);
  169. }
  170. retval->l = jobj;
  171. }
  172. else if (returnType == VOID) {
  173. if (methType == STATIC) {
  174. (*env)->CallStaticVoidMethodV(env, cls, mid, args);
  175. }
  176. else if (methType == INSTANCE) {
  177. (*env)->CallVoidMethodV(env, instObj, mid, args);
  178. }
  179. }
  180. else if (returnType == JBOOLEAN) {
  181. jboolean jbool = 0;
  182. if (methType == STATIC) {
  183. jbool = (*env)->CallStaticBooleanMethodV(env, cls, mid, args);
  184. }
  185. else if (methType == INSTANCE) {
  186. jbool = (*env)->CallBooleanMethodV(env, instObj, mid, args);
  187. }
  188. retval->z = jbool;
  189. }
  190. else if (returnType == JSHORT) {
  191. jshort js = 0;
  192. if (methType == STATIC) {
  193. js = (*env)->CallStaticShortMethodV(env, cls, mid, args);
  194. }
  195. else if (methType == INSTANCE) {
  196. js = (*env)->CallShortMethodV(env, instObj, mid, args);
  197. }
  198. retval->s = js;
  199. }
  200. else if (returnType == JLONG) {
  201. jlong jl = -1;
  202. if (methType == STATIC) {
  203. jl = (*env)->CallStaticLongMethodV(env, cls, mid, args);
  204. }
  205. else if (methType == INSTANCE) {
  206. jl = (*env)->CallLongMethodV(env, instObj, mid, args);
  207. }
  208. retval->j = jl;
  209. }
  210. else if (returnType == JINT) {
  211. jint ji = -1;
  212. if (methType == STATIC) {
  213. ji = (*env)->CallStaticIntMethodV(env, cls, mid, args);
  214. }
  215. else if (methType == INSTANCE) {
  216. ji = (*env)->CallIntMethodV(env, instObj, mid, args);
  217. }
  218. retval->i = ji;
  219. }
  220. va_end(args);
  221. jthr = (*env)->ExceptionOccurred(env);
  222. if (jthr != NULL) {
  223. if (exc != NULL)
  224. *exc = jthr;
  225. else
  226. (*env)->ExceptionDescribe(env);
  227. return -1;
  228. }
  229. return 0;
  230. }
  231. jarray constructNewArrayString(JNIEnv *env, Exc *exc, const char **elements, int size) {
  232. const char *className = "java/lang/String";
  233. jobjectArray result;
  234. int i;
  235. jclass arrCls = (*env)->FindClass(env, className);
  236. if (arrCls == NULL) {
  237. fprintf(stderr, "could not find class %s\n",className);
  238. return NULL; /* exception thrown */
  239. }
  240. result = (*env)->NewObjectArray(env, size, arrCls,
  241. NULL);
  242. if (result == NULL) {
  243. fprintf(stderr, "ERROR: could not construct new array\n");
  244. return NULL; /* out of memory error thrown */
  245. }
  246. for (i = 0; i < size; i++) {
  247. jstring jelem = (*env)->NewStringUTF(env,elements[i]);
  248. if (jelem == NULL) {
  249. fprintf(stderr, "ERROR: jelem == NULL\n");
  250. }
  251. (*env)->SetObjectArrayElement(env, result, i, jelem);
  252. (*env)->DeleteLocalRef(env, jelem);
  253. }
  254. return result;
  255. }
  256. jobject constructNewObjectOfClass(JNIEnv *env, Exc *exc, const char *className,
  257. const char *ctorSignature, ...)
  258. {
  259. va_list args;
  260. jclass cls;
  261. jmethodID mid;
  262. jobject jobj;
  263. jthrowable jthr;
  264. cls = globalClassReference(className, env);
  265. if (cls == NULL) {
  266. (*env)->ExceptionDescribe(env);
  267. return NULL;
  268. }
  269. mid = methodIdFromClass(className, "<init>", ctorSignature,
  270. INSTANCE, env);
  271. if (mid == NULL) {
  272. (*env)->ExceptionDescribe(env);
  273. return NULL;
  274. }
  275. va_start(args, ctorSignature);
  276. jobj = (*env)->NewObjectV(env, cls, mid, args);
  277. va_end(args);
  278. jthr = (*env)->ExceptionOccurred(env);
  279. if (jthr != NULL) {
  280. if (exc != NULL)
  281. *exc = jthr;
  282. else
  283. (*env)->ExceptionDescribe(env);
  284. }
  285. return jobj;
  286. }
  287. jmethodID methodIdFromClass(const char *className, const char *methName,
  288. const char *methSignature, MethType methType,
  289. JNIEnv *env)
  290. {
  291. jclass cls = globalClassReference(className, env);
  292. if (cls == NULL) {
  293. fprintf(stderr, "could not find class %s\n", className);
  294. return NULL;
  295. }
  296. jmethodID mid = 0;
  297. if (!validateMethodType(methType)) {
  298. fprintf(stderr, "invalid method type\n");
  299. return NULL;
  300. }
  301. if (methType == STATIC) {
  302. mid = (*env)->GetStaticMethodID(env, cls, methName, methSignature);
  303. }
  304. else if (methType == INSTANCE) {
  305. mid = (*env)->GetMethodID(env, cls, methName, methSignature);
  306. }
  307. if (mid == NULL) {
  308. fprintf(stderr, "could not find method %s from class %s with signature %s\n",methName, className, methSignature);
  309. }
  310. return mid;
  311. }
  312. jclass globalClassReference(const char *className, JNIEnv *env)
  313. {
  314. jclass clsLocalRef;
  315. jclass cls = searchEntryFromTable(className);
  316. if (cls) {
  317. return cls;
  318. }
  319. clsLocalRef = (*env)->FindClass(env,className);
  320. if (clsLocalRef == NULL) {
  321. (*env)->ExceptionDescribe(env);
  322. return NULL;
  323. }
  324. cls = (*env)->NewGlobalRef(env, clsLocalRef);
  325. if (cls == NULL) {
  326. (*env)->ExceptionDescribe(env);
  327. return NULL;
  328. }
  329. (*env)->DeleteLocalRef(env, clsLocalRef);
  330. insertEntryIntoTable(className, cls);
  331. return cls;
  332. }
  333. char *classNameOfObject(jobject jobj, JNIEnv *env) {
  334. jclass cls, clsClass;
  335. jmethodID mid;
  336. jstring str;
  337. const char *cstr;
  338. char *newstr;
  339. cls = (*env)->GetObjectClass(env, jobj);
  340. if (cls == NULL) {
  341. (*env)->ExceptionDescribe(env);
  342. return NULL;
  343. }
  344. clsClass = (*env)->FindClass(env, "java/lang/Class");
  345. if (clsClass == NULL) {
  346. (*env)->ExceptionDescribe(env);
  347. return NULL;
  348. }
  349. mid = (*env)->GetMethodID(env, clsClass, "getName", "()Ljava/lang/String;");
  350. if (mid == NULL) {
  351. (*env)->ExceptionDescribe(env);
  352. return NULL;
  353. }
  354. str = (*env)->CallObjectMethod(env, cls, mid);
  355. if (str == NULL) {
  356. (*env)->ExceptionDescribe(env);
  357. return NULL;
  358. }
  359. cstr = (*env)->GetStringUTFChars(env, str, NULL);
  360. newstr = strdup(cstr);
  361. (*env)->ReleaseStringUTFChars(env, str, cstr);
  362. if (newstr == NULL) {
  363. perror("classNameOfObject: strdup");
  364. return NULL;
  365. }
  366. return newstr;
  367. }
  368. /**
  369. * Get the global JNI environemnt.
  370. *
  371. * We only have to create the JVM once. After that, we can use it in
  372. * every thread. You must be holding the jvmMutex when you call this
  373. * function.
  374. *
  375. * @return The JNIEnv on success; error code otherwise
  376. */
  377. static JNIEnv* getGlobalJNIEnv(void)
  378. {
  379. const jsize vmBufLength = 1;
  380. JavaVM* vmBuf[vmBufLength];
  381. JNIEnv *env;
  382. jint rv = 0;
  383. jint noVMs = 0;
  384. rv = JNI_GetCreatedJavaVMs(&(vmBuf[0]), vmBufLength, &noVMs);
  385. if (rv != 0) {
  386. fprintf(stderr, "JNI_GetCreatedJavaVMs failed with error: %d\n", rv);
  387. return NULL;
  388. }
  389. if (noVMs == 0) {
  390. //Get the environment variables for initializing the JVM
  391. char *hadoopClassPath = getenv("CLASSPATH");
  392. if (hadoopClassPath == NULL) {
  393. fprintf(stderr, "Environment variable CLASSPATH not set!\n");
  394. return NULL;
  395. }
  396. char *hadoopClassPathVMArg = "-Djava.class.path=";
  397. size_t optHadoopClassPathLen = strlen(hadoopClassPath) +
  398. strlen(hadoopClassPathVMArg) + 1;
  399. char *optHadoopClassPath = malloc(sizeof(char)*optHadoopClassPathLen);
  400. snprintf(optHadoopClassPath, optHadoopClassPathLen,
  401. "%s%s", hadoopClassPathVMArg, hadoopClassPath);
  402. // Determine the # of LIBHDFS_OPTS args
  403. int noArgs = 1;
  404. char *hadoopJvmArgs = getenv("LIBHDFS_OPTS");
  405. char jvmArgDelims[] = " ";
  406. char *str, *token, *savePtr;
  407. if (hadoopJvmArgs != NULL) {
  408. hadoopJvmArgs = strdup(hadoopJvmArgs);
  409. for (noArgs = 1, str = hadoopJvmArgs; ; noArgs++, str = NULL) {
  410. token = strtok_r(str, jvmArgDelims, &savePtr);
  411. if (NULL == token) {
  412. break;
  413. }
  414. }
  415. free(hadoopJvmArgs);
  416. }
  417. // Now that we know the # args, populate the options array
  418. JavaVMOption options[noArgs];
  419. options[0].optionString = optHadoopClassPath;
  420. hadoopJvmArgs = getenv("LIBHDFS_OPTS");
  421. if (hadoopJvmArgs != NULL) {
  422. hadoopJvmArgs = strdup(hadoopJvmArgs);
  423. for (noArgs = 1, str = hadoopJvmArgs; ; noArgs++, str = NULL) {
  424. token = strtok_r(str, jvmArgDelims, &savePtr);
  425. if (NULL == token) {
  426. break;
  427. }
  428. options[noArgs].optionString = token;
  429. }
  430. }
  431. //Create the VM
  432. JavaVMInitArgs vm_args;
  433. JavaVM *vm;
  434. vm_args.version = JNI_VERSION_1_2;
  435. vm_args.options = options;
  436. vm_args.nOptions = noArgs;
  437. vm_args.ignoreUnrecognized = 1;
  438. rv = JNI_CreateJavaVM(&vm, (void*)&env, &vm_args);
  439. if (hadoopJvmArgs != NULL) {
  440. free(hadoopJvmArgs);
  441. }
  442. free(optHadoopClassPath);
  443. if (rv != 0) {
  444. fprintf(stderr, "Call to JNI_CreateJavaVM failed "
  445. "with error: %d\n", rv);
  446. return NULL;
  447. }
  448. }
  449. else {
  450. //Attach this thread to the VM
  451. JavaVM* vm = vmBuf[0];
  452. rv = (*vm)->AttachCurrentThread(vm, (void*)&env, 0);
  453. if (rv != 0) {
  454. fprintf(stderr, "Call to AttachCurrentThread "
  455. "failed with error: %d\n", rv);
  456. return NULL;
  457. }
  458. }
  459. return env;
  460. }
  461. /**
  462. * getJNIEnv: A helper function to get the JNIEnv* for the given thread.
  463. * If no JVM exists, then one will be created. JVM command line arguments
  464. * are obtained from the LIBHDFS_OPTS environment variable.
  465. *
  466. * Implementation note: we rely on POSIX thread-local storage (tls).
  467. * This allows us to associate a destructor function with each thread, that
  468. * will detach the thread from the Java VM when the thread terminates. If we
  469. * failt to do this, it will cause a memory leak.
  470. *
  471. * However, POSIX TLS is not the most efficient way to do things. It requires a
  472. * key to be initialized before it can be used. Since we don't know if this key
  473. * is initialized at the start of this function, we have to lock a mutex first
  474. * and check. Luckily, most operating systems support the more efficient
  475. * __thread construct, which is initialized by the linker.
  476. *
  477. * @param: None.
  478. * @return The JNIEnv* corresponding to the thread.
  479. */
  480. JNIEnv* getJNIEnv(void)
  481. {
  482. JNIEnv *env;
  483. struct hdfsTls *tls;
  484. int ret;
  485. #ifdef HAVE_BETTER_TLS
  486. static __thread struct hdfsTls *quickTls = NULL;
  487. if (quickTls)
  488. return quickTls->env;
  489. #endif
  490. pthread_mutex_lock(&jvmMutex);
  491. if (!gTlsKeyInitialized) {
  492. ret = pthread_key_create(&gTlsKey, hdfsThreadDestructor);
  493. if (ret) {
  494. pthread_mutex_unlock(&jvmMutex);
  495. fprintf(stderr, "getJNIEnv: pthread_key_create failed with "
  496. "error %d\n", ret);
  497. return NULL;
  498. }
  499. gTlsKeyInitialized = 1;
  500. }
  501. tls = pthread_getspecific(gTlsKey);
  502. if (tls) {
  503. pthread_mutex_unlock(&jvmMutex);
  504. return tls->env;
  505. }
  506. env = getGlobalJNIEnv();
  507. pthread_mutex_unlock(&jvmMutex);
  508. if (!env) {
  509. fprintf(stderr, "getJNIEnv: getGlobalJNIEnv failed\n");
  510. return NULL;
  511. }
  512. tls = calloc(1, sizeof(struct hdfsTls));
  513. if (!tls) {
  514. fprintf(stderr, "getJNIEnv: OOM allocating %zd bytes\n",
  515. sizeof(struct hdfsTls));
  516. return NULL;
  517. }
  518. tls->env = env;
  519. ret = pthread_setspecific(gTlsKey, tls);
  520. if (ret) {
  521. fprintf(stderr, "getJNIEnv: pthread_setspecific failed with "
  522. "error code %d\n", ret);
  523. hdfsThreadDestructor(tls);
  524. return NULL;
  525. }
  526. #ifdef HAVE_BETTER_TLS
  527. quickTls = tls;
  528. #endif
  529. return env;
  530. }