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