jni_helper.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  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 "exception.h"
  20. #include "jni_helper.h"
  21. #include "platform.h"
  22. #include "common/htable.h"
  23. #include "os/mutexes.h"
  24. #include "os/thread_local_storage.h"
  25. #include <errno.h>
  26. #include <dirent.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. static struct htable *gClassRefHTable = NULL;
  30. /** The Native return types that methods could return */
  31. #define JVOID 'V'
  32. #define JOBJECT 'L'
  33. #define JARRAYOBJECT '['
  34. #define JBOOLEAN 'Z'
  35. #define JBYTE 'B'
  36. #define JCHAR 'C'
  37. #define JSHORT 'S'
  38. #define JINT 'I'
  39. #define JLONG 'J'
  40. #define JFLOAT 'F'
  41. #define JDOUBLE 'D'
  42. /**
  43. * MAX_HASH_TABLE_ELEM: The maximum no. of entries in the hashtable.
  44. * It's set to 4096 to account for (classNames + No. of threads)
  45. */
  46. #define MAX_HASH_TABLE_ELEM 4096
  47. /**
  48. * Length of buffer for retrieving created JVMs. (We only ever create one.)
  49. */
  50. #define VM_BUF_LENGTH 1
  51. void destroyLocalReference(JNIEnv *env, jobject jObject)
  52. {
  53. if (jObject)
  54. (*env)->DeleteLocalRef(env, jObject);
  55. }
  56. static jthrowable validateMethodType(JNIEnv *env, MethType methType)
  57. {
  58. if (methType != STATIC && methType != INSTANCE) {
  59. return newRuntimeError(env, "validateMethodType(methType=%d): "
  60. "illegal method type.\n", methType);
  61. }
  62. return NULL;
  63. }
  64. jthrowable newJavaStr(JNIEnv *env, const char *str, jstring *out)
  65. {
  66. jstring jstr;
  67. if (!str) {
  68. /* Can't pass NULL to NewStringUTF: the result would be
  69. * implementation-defined. */
  70. *out = NULL;
  71. return NULL;
  72. }
  73. jstr = (*env)->NewStringUTF(env, str);
  74. if (!jstr) {
  75. /* If NewStringUTF returns NULL, an exception has been thrown,
  76. * which we need to handle. Probaly an OOM. */
  77. return getPendingExceptionAndClear(env);
  78. }
  79. *out = jstr;
  80. return NULL;
  81. }
  82. jthrowable newCStr(JNIEnv *env, jstring jstr, char **out)
  83. {
  84. const char *tmp;
  85. if (!jstr) {
  86. *out = NULL;
  87. return NULL;
  88. }
  89. tmp = (*env)->GetStringUTFChars(env, jstr, NULL);
  90. if (!tmp) {
  91. return getPendingExceptionAndClear(env);
  92. }
  93. *out = strdup(tmp);
  94. (*env)->ReleaseStringUTFChars(env, jstr, tmp);
  95. return NULL;
  96. }
  97. jthrowable invokeMethod(JNIEnv *env, jvalue *retval, MethType methType,
  98. jobject instObj, const char *className,
  99. const char *methName, const char *methSignature, ...)
  100. {
  101. va_list args;
  102. jclass cls;
  103. jmethodID mid;
  104. jthrowable jthr;
  105. const char *str;
  106. char returnType;
  107. jthr = validateMethodType(env, methType);
  108. if (jthr)
  109. return jthr;
  110. jthr = globalClassReference(className, env, &cls);
  111. if (jthr)
  112. return jthr;
  113. jthr = methodIdFromClass(className, methName, methSignature,
  114. methType, env, &mid);
  115. if (jthr)
  116. return jthr;
  117. str = methSignature;
  118. while (*str != ')') str++;
  119. str++;
  120. returnType = *str;
  121. va_start(args, methSignature);
  122. if (returnType == JOBJECT || returnType == JARRAYOBJECT) {
  123. jobject jobj = NULL;
  124. if (methType == STATIC) {
  125. jobj = (*env)->CallStaticObjectMethodV(env, cls, mid, args);
  126. }
  127. else if (methType == INSTANCE) {
  128. jobj = (*env)->CallObjectMethodV(env, instObj, mid, args);
  129. }
  130. retval->l = jobj;
  131. }
  132. else if (returnType == JVOID) {
  133. if (methType == STATIC) {
  134. (*env)->CallStaticVoidMethodV(env, cls, mid, args);
  135. }
  136. else if (methType == INSTANCE) {
  137. (*env)->CallVoidMethodV(env, instObj, mid, args);
  138. }
  139. }
  140. else if (returnType == JBOOLEAN) {
  141. jboolean jbool = 0;
  142. if (methType == STATIC) {
  143. jbool = (*env)->CallStaticBooleanMethodV(env, cls, mid, args);
  144. }
  145. else if (methType == INSTANCE) {
  146. jbool = (*env)->CallBooleanMethodV(env, instObj, mid, args);
  147. }
  148. retval->z = jbool;
  149. }
  150. else if (returnType == JSHORT) {
  151. jshort js = 0;
  152. if (methType == STATIC) {
  153. js = (*env)->CallStaticShortMethodV(env, cls, mid, args);
  154. }
  155. else if (methType == INSTANCE) {
  156. js = (*env)->CallShortMethodV(env, instObj, mid, args);
  157. }
  158. retval->s = js;
  159. }
  160. else if (returnType == JLONG) {
  161. jlong jl = -1;
  162. if (methType == STATIC) {
  163. jl = (*env)->CallStaticLongMethodV(env, cls, mid, args);
  164. }
  165. else if (methType == INSTANCE) {
  166. jl = (*env)->CallLongMethodV(env, instObj, mid, args);
  167. }
  168. retval->j = jl;
  169. }
  170. else if (returnType == JINT) {
  171. jint ji = -1;
  172. if (methType == STATIC) {
  173. ji = (*env)->CallStaticIntMethodV(env, cls, mid, args);
  174. }
  175. else if (methType == INSTANCE) {
  176. ji = (*env)->CallIntMethodV(env, instObj, mid, args);
  177. }
  178. retval->i = ji;
  179. }
  180. va_end(args);
  181. jthr = (*env)->ExceptionOccurred(env);
  182. if (jthr) {
  183. (*env)->ExceptionClear(env);
  184. return jthr;
  185. }
  186. return NULL;
  187. }
  188. jthrowable constructNewObjectOfClass(JNIEnv *env, jobject *out, const char *className,
  189. const char *ctorSignature, ...)
  190. {
  191. va_list args;
  192. jclass cls;
  193. jmethodID mid;
  194. jobject jobj;
  195. jthrowable jthr;
  196. jthr = globalClassReference(className, env, &cls);
  197. if (jthr)
  198. return jthr;
  199. jthr = methodIdFromClass(className, "<init>", ctorSignature,
  200. INSTANCE, env, &mid);
  201. if (jthr)
  202. return jthr;
  203. va_start(args, ctorSignature);
  204. jobj = (*env)->NewObjectV(env, cls, mid, args);
  205. va_end(args);
  206. if (!jobj)
  207. return getPendingExceptionAndClear(env);
  208. *out = jobj;
  209. return NULL;
  210. }
  211. jthrowable methodIdFromClass(const char *className, const char *methName,
  212. const char *methSignature, MethType methType,
  213. JNIEnv *env, jmethodID *out)
  214. {
  215. jclass cls;
  216. jthrowable jthr;
  217. jmethodID mid = 0;
  218. jthr = globalClassReference(className, env, &cls);
  219. if (jthr)
  220. return jthr;
  221. jthr = validateMethodType(env, methType);
  222. if (jthr)
  223. return jthr;
  224. if (methType == STATIC) {
  225. mid = (*env)->GetStaticMethodID(env, cls, methName, methSignature);
  226. }
  227. else if (methType == INSTANCE) {
  228. mid = (*env)->GetMethodID(env, cls, methName, methSignature);
  229. }
  230. if (mid == NULL) {
  231. fprintf(stderr, "could not find method %s from class %s with "
  232. "signature %s\n", methName, className, methSignature);
  233. return getPendingExceptionAndClear(env);
  234. }
  235. *out = mid;
  236. return NULL;
  237. }
  238. jthrowable globalClassReference(const char *className, JNIEnv *env, jclass *out)
  239. {
  240. jthrowable jthr = NULL;
  241. jclass local_clazz = NULL;
  242. jclass clazz = NULL;
  243. int ret;
  244. mutexLock(&hdfsHashMutex);
  245. if (!gClassRefHTable) {
  246. gClassRefHTable = htable_alloc(MAX_HASH_TABLE_ELEM, ht_hash_string,
  247. ht_compare_string);
  248. if (!gClassRefHTable) {
  249. jthr = newRuntimeError(env, "htable_alloc failed\n");
  250. goto done;
  251. }
  252. }
  253. clazz = htable_get(gClassRefHTable, className);
  254. if (clazz) {
  255. *out = clazz;
  256. goto done;
  257. }
  258. local_clazz = (*env)->FindClass(env,className);
  259. if (!local_clazz) {
  260. jthr = getPendingExceptionAndClear(env);
  261. goto done;
  262. }
  263. clazz = (*env)->NewGlobalRef(env, local_clazz);
  264. if (!clazz) {
  265. jthr = getPendingExceptionAndClear(env);
  266. goto done;
  267. }
  268. ret = htable_put(gClassRefHTable, (void*)className, clazz);
  269. if (ret) {
  270. jthr = newRuntimeError(env, "htable_put failed with error "
  271. "code %d\n", ret);
  272. goto done;
  273. }
  274. *out = clazz;
  275. jthr = NULL;
  276. done:
  277. mutexUnlock(&hdfsHashMutex);
  278. (*env)->DeleteLocalRef(env, local_clazz);
  279. if (jthr && clazz) {
  280. (*env)->DeleteGlobalRef(env, clazz);
  281. }
  282. return jthr;
  283. }
  284. jthrowable classNameOfObject(jobject jobj, JNIEnv *env, char **name)
  285. {
  286. jthrowable jthr;
  287. jclass cls, clsClass = NULL;
  288. jmethodID mid;
  289. jstring str = NULL;
  290. const char *cstr = NULL;
  291. char *newstr;
  292. cls = (*env)->GetObjectClass(env, jobj);
  293. if (cls == NULL) {
  294. jthr = getPendingExceptionAndClear(env);
  295. goto done;
  296. }
  297. clsClass = (*env)->FindClass(env, "java/lang/Class");
  298. if (clsClass == NULL) {
  299. jthr = getPendingExceptionAndClear(env);
  300. goto done;
  301. }
  302. mid = (*env)->GetMethodID(env, clsClass, "getName", "()Ljava/lang/String;");
  303. if (mid == NULL) {
  304. jthr = getPendingExceptionAndClear(env);
  305. goto done;
  306. }
  307. str = (*env)->CallObjectMethod(env, cls, mid);
  308. if (str == NULL) {
  309. jthr = getPendingExceptionAndClear(env);
  310. goto done;
  311. }
  312. cstr = (*env)->GetStringUTFChars(env, str, NULL);
  313. if (!cstr) {
  314. jthr = getPendingExceptionAndClear(env);
  315. goto done;
  316. }
  317. newstr = strdup(cstr);
  318. if (newstr == NULL) {
  319. jthr = newRuntimeError(env, "classNameOfObject: out of memory");
  320. goto done;
  321. }
  322. *name = newstr;
  323. jthr = NULL;
  324. done:
  325. destroyLocalReference(env, cls);
  326. destroyLocalReference(env, clsClass);
  327. if (str) {
  328. if (cstr)
  329. (*env)->ReleaseStringUTFChars(env, str, cstr);
  330. (*env)->DeleteLocalRef(env, str);
  331. }
  332. return jthr;
  333. }
  334. /**
  335. * For the given path, expand it by filling in with all *.jar or *.JAR files,
  336. * separated by PATH_SEPARATOR. Assumes that expanded is big enough to hold the
  337. * string, eg allocated after using this function with expanded=NULL to get the
  338. * right size. Also assumes that the path ends with a "/.". The length of the
  339. * expanded path is returned, which includes space at the end for either a
  340. * PATH_SEPARATOR or null terminator.
  341. */
  342. static ssize_t wildcard_expandPath(const char* path, char* expanded)
  343. {
  344. struct dirent* file;
  345. char* dest = expanded;
  346. ssize_t length = 0;
  347. size_t pathLength = strlen(path);
  348. DIR* dir;
  349. dir = opendir(path);
  350. if (dir != NULL) {
  351. // can open dir so try to match with all *.jar and *.JAR entries
  352. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  353. printf("wildcard_expandPath: %s\n", path);
  354. #endif
  355. errno = 0;
  356. while ((file = readdir(dir)) != NULL) {
  357. const char* filename = file->d_name;
  358. const size_t filenameLength = strlen(filename);
  359. const char* jarExtension;
  360. // If filename is smaller than 4 characters then it can not possibly
  361. // have extension ".jar" or ".JAR"
  362. if (filenameLength < 4) {
  363. continue;
  364. }
  365. jarExtension = &filename[filenameLength-4];
  366. if ((strcmp(jarExtension, ".jar") == 0) ||
  367. (strcmp(jarExtension, ".JAR") == 0)) {
  368. // pathLength includes an extra '.' which we'll use for either
  369. // separator or null termination
  370. length += pathLength + filenameLength;
  371. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  372. printf("wildcard_scanPath:\t%s\t:\t%zd\n", filename, length);
  373. #endif
  374. if (expanded != NULL) {
  375. // pathLength includes an extra '.'
  376. strncpy(dest, path, pathLength-1);
  377. dest += pathLength - 1;
  378. strncpy(dest, filename, filenameLength);
  379. dest += filenameLength;
  380. *dest = PATH_SEPARATOR;
  381. dest++;
  382. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  383. printf("wildcard_expandPath:\t%s\t:\t%s\n",
  384. filename, expanded);
  385. #endif
  386. }
  387. }
  388. }
  389. if (errno != 0) {
  390. fprintf(stderr, "wildcard_expandPath: on readdir %s: %s\n",
  391. path, strerror(errno));
  392. length = -1;
  393. }
  394. if (closedir(dir) != 0) {
  395. fprintf(stderr, "wildcard_expandPath: on closedir %s: %s\n",
  396. path, strerror(errno));
  397. }
  398. } else if ((errno != EACCES) && (errno != ENOENT) && (errno != ENOTDIR)) {
  399. // can not opendir due to an error we can not handle
  400. fprintf(stderr, "wildcard_expandPath: on opendir %s: %s\n", path,
  401. strerror(errno));
  402. length = -1;
  403. }
  404. if (length == 0) {
  405. // either we failed to open dir due to EACCESS, ENOENT, or ENOTDIR, or
  406. // we did not find any file that matches *.jar or *.JAR
  407. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  408. fprintf(stderr, "wildcard_expandPath: can not expand %.*s*: %s\n",
  409. (int)(pathLength-1), path, strerror(errno));
  410. #endif
  411. // in this case, the wildcard expansion is the same as the original
  412. // +1 for PATH_SEPARTOR or null termination
  413. length = pathLength + 1;
  414. if (expanded != NULL) {
  415. // pathLength includes an extra '.'
  416. strncpy(dest, path, pathLength-1);
  417. dest += pathLength-1;
  418. *dest = '*'; // restore wildcard
  419. dest++;
  420. *dest = PATH_SEPARATOR;
  421. dest++;
  422. }
  423. }
  424. return length;
  425. }
  426. /**
  427. * Helper to expand classpaths. Returns the total length of the expanded
  428. * classpath. If expandedClasspath is not NULL, then fills that with the
  429. * expanded classpath. It assumes that expandedClasspath is of correct size, eg
  430. * allocated after using this function with expandedClasspath=NULL to get the
  431. * right size.
  432. */
  433. static ssize_t getClassPath_helper(const char *classpath, char* expandedClasspath)
  434. {
  435. ssize_t length;
  436. ssize_t retval;
  437. char* expandedCP_curr;
  438. char* cp_token;
  439. char* classpath_dup;
  440. classpath_dup = strdup(classpath);
  441. if (classpath_dup == NULL) {
  442. fprintf(stderr, "getClassPath_helper: failed strdup: %s\n",
  443. strerror(errno));
  444. return -1;
  445. }
  446. length = 0;
  447. // expandedCP_curr is the current pointer
  448. expandedCP_curr = expandedClasspath;
  449. cp_token = strtok(classpath_dup, PATH_SEPARATOR_STR);
  450. while (cp_token != NULL) {
  451. size_t tokenlen;
  452. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  453. printf("%s\n", cp_token);
  454. #endif
  455. tokenlen = strlen(cp_token);
  456. // We only expand if token ends with "/*"
  457. if ((tokenlen > 1) &&
  458. (cp_token[tokenlen-1] == '*') && (cp_token[tokenlen-2] == '/')) {
  459. // replace the '*' with '.' so that we don't have to allocate another
  460. // string for passing to opendir() in wildcard_expandPath()
  461. cp_token[tokenlen-1] = '.';
  462. retval = wildcard_expandPath(cp_token, expandedCP_curr);
  463. if (retval < 0) {
  464. free(classpath_dup);
  465. return -1;
  466. }
  467. length += retval;
  468. if (expandedCP_curr != NULL) {
  469. expandedCP_curr += retval;
  470. }
  471. } else {
  472. // +1 for path separator or null terminator
  473. length += tokenlen + 1;
  474. if (expandedCP_curr != NULL) {
  475. strncpy(expandedCP_curr, cp_token, tokenlen);
  476. expandedCP_curr += tokenlen;
  477. *expandedCP_curr = PATH_SEPARATOR;
  478. expandedCP_curr++;
  479. }
  480. }
  481. cp_token = strtok(NULL, PATH_SEPARATOR_STR);
  482. }
  483. // Fix the last ':' and use it to null terminate
  484. if (expandedCP_curr != NULL) {
  485. expandedCP_curr--;
  486. *expandedCP_curr = '\0';
  487. }
  488. free(classpath_dup);
  489. return length;
  490. }
  491. /**
  492. * Gets the classpath. Wild card entries are resolved only if the entry ends
  493. * with "/\*" (backslash to escape commenting) to match against .jar and .JAR.
  494. * All other wild card entries (eg /path/to/dir/\*foo*) are not resolved,
  495. * following JAVA default behavior, see:
  496. * https://docs.oracle.com/javase/8/docs/technotes/tools/unix/classpath.html
  497. */
  498. static char* getClassPath()
  499. {
  500. char* classpath;
  501. char* expandedClasspath;
  502. ssize_t length;
  503. ssize_t retval;
  504. classpath = getenv("CLASSPATH");
  505. if (classpath == NULL) {
  506. return NULL;
  507. }
  508. // First, get the total size of the string we will need for the expanded
  509. // classpath
  510. length = getClassPath_helper(classpath, NULL);
  511. if (length < 0) {
  512. return NULL;
  513. }
  514. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  515. printf("+++++++++++++++++\n");
  516. #endif
  517. // we don't have to do anything if classpath has no valid wildcards
  518. // we get length = 0 when CLASSPATH is set but empty
  519. // if CLASSPATH is not empty, then length includes null terminator
  520. // if length of expansion is same as original, then return a duplicate of
  521. // original since expansion can only be longer
  522. if ((length == 0) || ((length - 1) == strlen(classpath))) {
  523. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  524. if ((length == 0) && (strlen(classpath) != 0)) {
  525. fprintf(stderr, "Something went wrong with getting the wildcard \
  526. expansion length\n" );
  527. }
  528. #endif
  529. expandedClasspath = strdup(classpath);
  530. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  531. printf("Expanded classpath=%s\n", expandedClasspath);
  532. #endif
  533. return expandedClasspath;
  534. }
  535. // Allocte memory for expanded classpath string
  536. expandedClasspath = calloc(length, sizeof(char));
  537. if (expandedClasspath == NULL) {
  538. fprintf(stderr, "getClassPath: failed calloc: %s\n", strerror(errno));
  539. return NULL;
  540. }
  541. // Actual expansion
  542. retval = getClassPath_helper(classpath, expandedClasspath);
  543. if (retval < 0) {
  544. free(expandedClasspath);
  545. return NULL;
  546. }
  547. // This should not happen, but dotting i's and crossing t's
  548. if (retval != length) {
  549. fprintf(stderr,
  550. "Expected classpath expansion length to be %zu but instead got %zu\n",
  551. length, retval);
  552. free(expandedClasspath);
  553. return NULL;
  554. }
  555. #ifdef _LIBHDFS_JNI_HELPER_DEBUGGING_ON_
  556. printf("===============\n");
  557. printf("Allocated %zd for expanding classpath\n", length);
  558. printf("Used %zu for expanding classpath\n", strlen(expandedClasspath) + 1);
  559. printf("Expanded classpath=%s\n", expandedClasspath);
  560. #endif
  561. return expandedClasspath;
  562. }
  563. /**
  564. * Get the global JNI environemnt.
  565. *
  566. * We only have to create the JVM once. After that, we can use it in
  567. * every thread. You must be holding the jvmMutex when you call this
  568. * function.
  569. *
  570. * @return The JNIEnv on success; error code otherwise
  571. */
  572. static JNIEnv* getGlobalJNIEnv(void)
  573. {
  574. JavaVM* vmBuf[VM_BUF_LENGTH];
  575. JNIEnv *env;
  576. jint rv = 0;
  577. jint noVMs = 0;
  578. jthrowable jthr;
  579. char *hadoopClassPath;
  580. const char *hadoopClassPathVMArg = "-Djava.class.path=";
  581. size_t optHadoopClassPathLen;
  582. char *optHadoopClassPath;
  583. int noArgs = 1;
  584. char *hadoopJvmArgs;
  585. char jvmArgDelims[] = " ";
  586. char *str, *token, *savePtr;
  587. JavaVMInitArgs vm_args;
  588. JavaVM *vm;
  589. JavaVMOption *options;
  590. rv = JNI_GetCreatedJavaVMs(&(vmBuf[0]), VM_BUF_LENGTH, &noVMs);
  591. if (rv != 0) {
  592. fprintf(stderr, "JNI_GetCreatedJavaVMs failed with error: %d\n", rv);
  593. return NULL;
  594. }
  595. if (noVMs == 0) {
  596. //Get the environment variables for initializing the JVM
  597. hadoopClassPath = getClassPath();
  598. if (hadoopClassPath == NULL) {
  599. fprintf(stderr, "Environment variable CLASSPATH not set!\n");
  600. return NULL;
  601. }
  602. optHadoopClassPathLen = strlen(hadoopClassPath) +
  603. strlen(hadoopClassPathVMArg) + 1;
  604. optHadoopClassPath = malloc(sizeof(char)*optHadoopClassPathLen);
  605. snprintf(optHadoopClassPath, optHadoopClassPathLen,
  606. "%s%s", hadoopClassPathVMArg, hadoopClassPath);
  607. free(hadoopClassPath);
  608. // Determine the # of LIBHDFS_OPTS args
  609. hadoopJvmArgs = getenv("LIBHDFS_OPTS");
  610. if (hadoopJvmArgs != NULL) {
  611. hadoopJvmArgs = strdup(hadoopJvmArgs);
  612. for (noArgs = 1, str = hadoopJvmArgs; ; noArgs++, str = NULL) {
  613. token = strtok_r(str, jvmArgDelims, &savePtr);
  614. if (NULL == token) {
  615. break;
  616. }
  617. }
  618. free(hadoopJvmArgs);
  619. }
  620. // Now that we know the # args, populate the options array
  621. options = calloc(noArgs, sizeof(JavaVMOption));
  622. if (!options) {
  623. fputs("Call to calloc failed\n", stderr);
  624. free(optHadoopClassPath);
  625. return NULL;
  626. }
  627. options[0].optionString = optHadoopClassPath;
  628. hadoopJvmArgs = getenv("LIBHDFS_OPTS");
  629. if (hadoopJvmArgs != NULL) {
  630. hadoopJvmArgs = strdup(hadoopJvmArgs);
  631. for (noArgs = 1, str = hadoopJvmArgs; ; noArgs++, str = NULL) {
  632. token = strtok_r(str, jvmArgDelims, &savePtr);
  633. if (NULL == token) {
  634. break;
  635. }
  636. options[noArgs].optionString = token;
  637. }
  638. }
  639. //Create the VM
  640. vm_args.version = JNI_VERSION_1_2;
  641. vm_args.options = options;
  642. vm_args.nOptions = noArgs;
  643. vm_args.ignoreUnrecognized = 1;
  644. rv = JNI_CreateJavaVM(&vm, (void*)&env, &vm_args);
  645. if (hadoopJvmArgs != NULL) {
  646. free(hadoopJvmArgs);
  647. }
  648. free(optHadoopClassPath);
  649. free(options);
  650. if (rv != 0) {
  651. fprintf(stderr, "Call to JNI_CreateJavaVM failed "
  652. "with error: %d\n", rv);
  653. return NULL;
  654. }
  655. jthr = invokeMethod(env, NULL, STATIC, NULL,
  656. "org/apache/hadoop/fs/FileSystem",
  657. "loadFileSystems", "()V");
  658. if (jthr) {
  659. printExceptionAndFree(env, jthr, PRINT_EXC_ALL, "loadFileSystems");
  660. }
  661. }
  662. else {
  663. //Attach this thread to the VM
  664. vm = vmBuf[0];
  665. rv = (*vm)->AttachCurrentThread(vm, (void*)&env, 0);
  666. if (rv != 0) {
  667. fprintf(stderr, "Call to AttachCurrentThread "
  668. "failed with error: %d\n", rv);
  669. return NULL;
  670. }
  671. }
  672. return env;
  673. }
  674. /**
  675. * getJNIEnv: A helper function to get the JNIEnv* for the given thread.
  676. * If no JVM exists, then one will be created. JVM command line arguments
  677. * are obtained from the LIBHDFS_OPTS environment variable.
  678. *
  679. * Implementation note: we rely on POSIX thread-local storage (tls).
  680. * This allows us to associate a destructor function with each thread, that
  681. * will detach the thread from the Java VM when the thread terminates. If we
  682. * failt to do this, it will cause a memory leak.
  683. *
  684. * However, POSIX TLS is not the most efficient way to do things. It requires a
  685. * key to be initialized before it can be used. Since we don't know if this key
  686. * is initialized at the start of this function, we have to lock a mutex first
  687. * and check. Luckily, most operating systems support the more efficient
  688. * __thread construct, which is initialized by the linker.
  689. *
  690. * @param: None.
  691. * @return The JNIEnv* corresponding to the thread.
  692. */
  693. JNIEnv* getJNIEnv(void)
  694. {
  695. JNIEnv *env;
  696. THREAD_LOCAL_STORAGE_GET_QUICK();
  697. mutexLock(&jvmMutex);
  698. if (threadLocalStorageGet(&env)) {
  699. mutexUnlock(&jvmMutex);
  700. return NULL;
  701. }
  702. if (env) {
  703. mutexUnlock(&jvmMutex);
  704. return env;
  705. }
  706. env = getGlobalJNIEnv();
  707. mutexUnlock(&jvmMutex);
  708. if (!env) {
  709. fprintf(stderr, "getJNIEnv: getGlobalJNIEnv failed\n");
  710. return NULL;
  711. }
  712. if (threadLocalStorageSet(env)) {
  713. return NULL;
  714. }
  715. THREAD_LOCAL_STORAGE_SET_QUICK(env);
  716. return env;
  717. }
  718. int javaObjectIsOfClass(JNIEnv *env, jobject obj, const char *name)
  719. {
  720. jclass clazz;
  721. int ret;
  722. clazz = (*env)->FindClass(env, name);
  723. if (!clazz) {
  724. printPendingExceptionAndFree(env, PRINT_EXC_ALL,
  725. "javaObjectIsOfClass(%s)", name);
  726. return -1;
  727. }
  728. ret = (*env)->IsInstanceOf(env, obj, clazz);
  729. (*env)->DeleteLocalRef(env, clazz);
  730. return ret == JNI_TRUE ? 1 : 0;
  731. }
  732. jthrowable hadoopConfSetStr(JNIEnv *env, jobject jConfiguration,
  733. const char *key, const char *value)
  734. {
  735. jthrowable jthr;
  736. jstring jkey = NULL, jvalue = NULL;
  737. jthr = newJavaStr(env, key, &jkey);
  738. if (jthr)
  739. goto done;
  740. jthr = newJavaStr(env, value, &jvalue);
  741. if (jthr)
  742. goto done;
  743. jthr = invokeMethod(env, NULL, INSTANCE, jConfiguration,
  744. "org/apache/hadoop/conf/Configuration", "set",
  745. "(Ljava/lang/String;Ljava/lang/String;)V",
  746. jkey, jvalue);
  747. if (jthr)
  748. goto done;
  749. done:
  750. (*env)->DeleteLocalRef(env, jkey);
  751. (*env)->DeleteLocalRef(env, jvalue);
  752. return jthr;
  753. }
  754. jthrowable fetchEnumInstance(JNIEnv *env, const char *className,
  755. const char *valueName, jobject *out)
  756. {
  757. jclass clazz;
  758. jfieldID fieldId;
  759. jobject jEnum;
  760. char prettyClass[256];
  761. clazz = (*env)->FindClass(env, className);
  762. if (!clazz) {
  763. return newRuntimeError(env, "fetchEnum(%s, %s): failed to find class.",
  764. className, valueName);
  765. }
  766. if (snprintf(prettyClass, sizeof(prettyClass), "L%s;", className)
  767. >= sizeof(prettyClass)) {
  768. return newRuntimeError(env, "fetchEnum(%s, %s): class name too long.",
  769. className, valueName);
  770. }
  771. fieldId = (*env)->GetStaticFieldID(env, clazz, valueName, prettyClass);
  772. if (!fieldId) {
  773. return getPendingExceptionAndClear(env);
  774. }
  775. jEnum = (*env)->GetStaticObjectField(env, clazz, fieldId);
  776. if (!jEnum) {
  777. return getPendingExceptionAndClear(env);
  778. }
  779. *out = jEnum;
  780. return NULL;
  781. }