| /******************************************************************************* |
| * Copyright (c) 2006, 2009 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Andrew Niefer |
| *******************************************************************************/ |
| |
| #include "eclipseJNI.h" |
| #include "eclipseCommon.h" |
| #include "eclipseOS.h" |
| #include "eclipseShm.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| |
| static _TCHAR* failedToLoadLibrary = _T_ECLIPSE("Failed to load the JNI shared library \"%s\".\n"); |
| static _TCHAR* createVMSymbolNotFound = _T_ECLIPSE("The JVM shared library \"%s\"\ndoes not contain the JNI_CreateJavaVM symbol.\n"); |
| static _TCHAR* failedCreateVM = _T_ECLIPSE("Failed to create the Java Virtual Machine.\n"); |
| static _TCHAR* internalExpectedVMArgs = _T_ECLIPSE("Internal Error, the JVM argument list is empty.\n"); |
| static _TCHAR* mainClassNotFound = _T_ECLIPSE("Failed to find a Main Class in \"%s\".\n"); |
| |
| static JNINativeMethod natives[] = {{"_update_splash", "()V", (void *)&update_splash}, |
| {"_get_splash_handle", "()J", (void *)&get_splash_handle}, |
| {"_set_exit_data", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)&set_exit_data}, |
| {"_set_launcher_info", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)&set_launcher_info}, |
| {"_show_splash", "(Ljava/lang/String;)V", (void *)&show_splash}, |
| {"_takedown_splash", "()V", (void *)&takedown_splash}}; |
| |
| /* local methods */ |
| static jstring newJavaString(JNIEnv *env, _TCHAR * str); |
| static void registerNatives(JNIEnv *env); |
| static int shouldShutdown(JNIEnv *env); |
| static void JNI_ReleaseStringChars(JNIEnv *env, jstring s, const _TCHAR* data); |
| static const _TCHAR* JNI_GetStringChars(JNIEnv *env, jstring str); |
| static char * getMainClass(JNIEnv *env, _TCHAR * jarFile); |
| static void setLibraryLocation(JNIEnv *env, jobject obj); |
| |
| static JavaVM * jvm = 0; |
| static JNIEnv *env = 0; |
| |
| /* cache String class and methods to avoid looking them up all the time */ |
| static jclass string_class = NULL; |
| #if !defined(UNICODE) && !defined(MACOSX) |
| static jmethodID string_getBytesMethod = NULL; |
| static jmethodID string_ctor = NULL; |
| #endif |
| |
| /* JNI Callback methods */ |
| JNIEXPORT void JNICALL set_exit_data(JNIEnv * env, jobject obj, jstring id, jstring s){ |
| const _TCHAR* data = NULL; |
| const _TCHAR* sharedId = NULL; |
| size_t length; |
| |
| if(s != NULL) { |
| length = (*env)->GetStringLength(env, s); |
| if(!(*env)->ExceptionOccurred(env)) { |
| data = JNI_GetStringChars(env, s); |
| if (data != NULL) { |
| if(id != NULL) { |
| sharedId = JNI_GetStringChars(env, id); |
| if(sharedId != NULL) { |
| setSharedData(sharedId, data); |
| JNI_ReleaseStringChars(env, id, sharedId); |
| } |
| } else { |
| exitData = malloc((length + 1) * sizeof(_TCHAR*)); |
| _tcsncpy( exitData, data, length); |
| exitData[length] = _T_ECLIPSE('\0'); |
| } |
| JNI_ReleaseStringChars(env, s, data); |
| } |
| } |
| if(data == NULL && sharedId == NULL) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| } |
| |
| JNIEXPORT void JNICALL set_launcher_info(JNIEnv * env, jobject obj, jstring launcher, jstring name){ |
| const _TCHAR* launcherPath = NULL; |
| const _TCHAR* launcherName = NULL; |
| |
| if (launcher != NULL) { |
| launcherPath = JNI_GetStringChars(env, launcher); |
| if (launcherPath != NULL) { |
| setProgramPath(_tcsdup(launcherPath)); |
| JNI_ReleaseStringChars(env, launcher, launcherPath); |
| } |
| } |
| |
| if (name != NULL) { |
| launcherName = JNI_GetStringChars(env, name); |
| if (launcherName != NULL) { |
| setOfficialName(_tcsdup(launcherName)); |
| JNI_ReleaseStringChars(env, name, launcherName); |
| } |
| } |
| } |
| |
| |
| JNIEXPORT void JNICALL update_splash(JNIEnv * env, jobject obj){ |
| dispatchMessages(); |
| } |
| |
| JNIEXPORT jlong JNICALL get_splash_handle(JNIEnv * env, jobject obj){ |
| return getSplashHandle(); |
| } |
| |
| JNIEXPORT void JNICALL show_splash(JNIEnv * env, jobject obj, jstring s){ |
| const _TCHAR* data = NULL; |
| |
| setLibraryLocation(env, obj); |
| |
| if(s != NULL) { |
| data = JNI_GetStringChars(env, s); |
| if(data != NULL) { |
| showSplash(data); |
| JNI_ReleaseStringChars(env, s, data); |
| } else { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| } |
| |
| JNIEXPORT void JNICALL takedown_splash(JNIEnv * env, jobject obj){ |
| takeDownSplash(); |
| } |
| |
| /* |
| * On AIX we need the location of the eclipse shared library so that we |
| * can find the libeclipse-motif.so library. Reach into the JNIBridge |
| * object to get the "library" field. |
| */ |
| static void setLibraryLocation(JNIEnv * env, jobject obj) { |
| jclass bridge = (*env)->FindClass(env, "org/eclipse/equinox/launcher/JNIBridge"); |
| if (bridge != NULL) { |
| jfieldID libraryField = (*env)->GetFieldID(env, bridge, "library", "Ljava/lang/String;"); |
| if (libraryField != NULL) { |
| jstring stringObject = (jstring) (*env)->GetObjectField(env, obj, libraryField); |
| if (stringObject != NULL) { |
| const _TCHAR * str = JNI_GetStringChars(env, stringObject); |
| eclipseLibrary = _tcsdup(str); |
| JNI_ReleaseStringChars(env, stringObject, str); |
| } |
| } |
| } |
| if( (*env)->ExceptionOccurred(env) != 0 ){ |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| |
| static void registerNatives(JNIEnv *env) { |
| jclass bridge = (*env)->FindClass(env, "org/eclipse/equinox/launcher/JNIBridge"); |
| if(bridge != NULL) { |
| int numNatives = sizeof(natives) / sizeof(natives[0]); |
| (*env)->RegisterNatives(env, bridge, natives, numNatives); |
| } |
| if( (*env)->ExceptionOccurred(env) != 0 ){ |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| |
| |
| /* Get a _TCHAR* from a jstring, string should be released later with JNI_ReleaseStringChars */ |
| static const _TCHAR * JNI_GetStringChars(JNIEnv *env, jstring str) { |
| const _TCHAR * result = NULL; |
| #ifdef UNICODE |
| /* GetStringChars is not null terminated, make a copy */ |
| const _TCHAR * stringChars = (*env)->GetStringChars(env, str, 0); |
| int length = (*env)->GetStringLength(env, str); |
| _TCHAR * copy = malloc( (length + 1) * sizeof(_TCHAR)); |
| _tcsncpy(copy, stringChars, length); |
| copy[length] = _T_ECLIPSE('\0'); |
| (*env)->ReleaseStringChars(env, str, stringChars); |
| result = copy; |
| #elif MACOSX |
| /* Use UTF on the Mac */ |
| result = (*env)->GetStringUTFChars(env, str, 0); |
| #else |
| /* Other platforms, use java's default encoding */ |
| _TCHAR* buffer = NULL; |
| if (string_class == NULL) |
| string_class = (*env)->FindClass(env, "java/lang/String"); |
| if (string_class != NULL) { |
| if (string_getBytesMethod == NULL) |
| string_getBytesMethod = (*env)->GetMethodID(env, string_class, "getBytes", "()[B"); |
| if (string_getBytesMethod != NULL) { |
| jbyteArray bytes = (*env)->CallObjectMethod(env, str, string_getBytesMethod); |
| if (!(*env)->ExceptionOccurred(env)) { |
| jsize length = (*env)->GetArrayLength(env, bytes); |
| buffer = malloc( (length + 1) * sizeof(_TCHAR*)); |
| (*env)->GetByteArrayRegion(env, bytes, 0, length, (jbyte*)buffer); |
| buffer[length] = 0; |
| } |
| (*env)->DeleteLocalRef(env, bytes); |
| } |
| } |
| if(buffer == NULL) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| result = buffer; |
| #endif |
| return result; |
| } |
| |
| /* Release the string that was obtained using JNI_GetStringChars */ |
| static void JNI_ReleaseStringChars(JNIEnv *env, jstring s, const _TCHAR* data) { |
| #ifdef UNICODE |
| free((_TCHAR*)data); |
| #elif MACOSX |
| (*env)->ReleaseStringUTFChars(env, s, data); |
| #else |
| free((_TCHAR*)data); |
| #endif |
| } |
| |
| static jstring newJavaString(JNIEnv *env, _TCHAR * str) |
| { |
| jstring newString = NULL; |
| #ifdef UNICODE |
| size_t length = _tcslen(str); |
| newString = (*env)->NewString(env, str, length); |
| #elif MACOSX |
| newString = (*env)->NewStringUTF(env, str); |
| #else |
| size_t length = _tcslen(str); |
| jbyteArray bytes = (*env)->NewByteArray(env, length); |
| if(bytes != NULL) { |
| (*env)->SetByteArrayRegion(env, bytes, 0, length, (jbyte *)str); |
| if (!(*env)->ExceptionOccurred(env)) { |
| if (string_class == NULL) |
| string_class = (*env)->FindClass(env, "java/lang/String"); |
| if(string_class != NULL) { |
| if (string_ctor == NULL) |
| string_ctor = (*env)->GetMethodID(env, string_class, "<init>", "([B)V"); |
| if(string_ctor != NULL) { |
| newString = (*env)->NewObject(env, string_class, string_ctor, bytes); |
| } |
| } |
| } |
| (*env)->DeleteLocalRef(env, bytes); |
| } |
| #endif |
| if(newString == NULL) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| return newString; |
| } |
| |
| static jobjectArray createRunArgs( JNIEnv *env, _TCHAR * args[] ) { |
| int index = 0, length = -1; |
| jobjectArray stringArray = NULL; |
| jstring string; |
| |
| /*count the number of elements first*/ |
| while(args[++length] != NULL); |
| |
| if (string_class == NULL) |
| string_class = (*env)->FindClass(env, "java/lang/String"); |
| if(string_class != NULL) { |
| stringArray = (*env)->NewObjectArray(env, length, string_class, 0); |
| if(stringArray != NULL) { |
| for( index = 0; index < length; index++) { |
| string = newJavaString(env, args[index]); |
| if(string != NULL) { |
| (*env)->SetObjectArrayElement(env, stringArray, index, string); |
| (*env)->DeleteLocalRef(env, string); |
| } else { |
| (*env)->DeleteLocalRef(env, stringArray); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| return NULL; |
| } |
| } |
| } |
| } |
| if(stringArray == NULL) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| return stringArray; |
| } |
| |
| JavaResults * startJavaJNI( _TCHAR* libPath, _TCHAR* vmArgs[], _TCHAR* progArgs[], _TCHAR* jarFile ) |
| { |
| int i; |
| int numVMArgs = -1; |
| void * jniLibrary; |
| JNI_createJavaVM createJavaVM; |
| JavaVMInitArgs init_args; |
| JavaVMOption * options; |
| char * mainClassName = NULL; |
| JavaResults * results = NULL; |
| |
| /* JNI reflection */ |
| jclass mainClass = NULL; /* The Main class to load */ |
| jmethodID mainConstructor = NULL; /* Main's default constructor Main() */ |
| jobject mainObject = NULL; /* An instantiation of the main class */ |
| jmethodID runMethod = NULL; /* Main.run(String[]) */ |
| jobjectArray methodArgs = NULL; /* Arguments to pass to run */ |
| |
| results = malloc(sizeof(JavaResults)); |
| memset(results, 0, sizeof(JavaResults)); |
| |
| jniLibrary = loadLibrary(libPath); |
| if(jniLibrary == NULL) { |
| results->launchResult = -1; |
| results->errorMessage = malloc((_tcslen(failedToLoadLibrary) + _tcslen(libPath) + 1) * sizeof(_TCHAR)); |
| _stprintf(results->errorMessage, failedToLoadLibrary, libPath); |
| return results; /*error*/ |
| } |
| |
| createJavaVM = (JNI_createJavaVM)findSymbol(jniLibrary, _T_ECLIPSE("JNI_CreateJavaVM")); |
| if(createJavaVM == NULL) { |
| results->launchResult = -2; |
| results->errorMessage = malloc((_tcslen(createVMSymbolNotFound) + _tcslen(libPath) + 1) * sizeof(_TCHAR)); |
| _stprintf(results->errorMessage, createVMSymbolNotFound, libPath); |
| return results; /*error*/ |
| } |
| |
| /* count the vm args */ |
| while(vmArgs[++numVMArgs] != NULL) {} |
| |
| if(numVMArgs <= 0) { |
| /*error, we expect at least the required vm arg */ |
| results->launchResult = -3; |
| results->errorMessage = _tcsdup(internalExpectedVMArgs); |
| return results; |
| } |
| |
| options = malloc(numVMArgs * sizeof(JavaVMOption)); |
| for(i = 0; i < numVMArgs; i++){ |
| options[i].optionString = toNarrow(vmArgs[i]); |
| options[i].extraInfo = 0; |
| } |
| |
| #ifdef MACOSX |
| init_args.version = JNI_VERSION_1_4; |
| #else |
| init_args.version = JNI_VERSION_1_2; |
| #endif |
| init_args.options = options; |
| init_args.nOptions = numVMArgs; |
| init_args.ignoreUnrecognized = JNI_TRUE; |
| |
| if( createJavaVM(&jvm, &env, &init_args) == 0 ) { |
| registerNatives(env); |
| |
| mainClassName = getMainClass(env, jarFile); |
| if (mainClassName != NULL) { |
| mainClass = (*env)->FindClass(env, mainClassName); |
| free(mainClassName); |
| } |
| |
| if (mainClass == NULL) { |
| if ((*env)->ExceptionOccurred(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| mainClass = (*env)->FindClass(env, "org/eclipse/equinox/launcher/Main"); |
| } |
| |
| if(mainClass != NULL) { |
| results->launchResult = -6; /* this will be reset to 0 below on success */ |
| mainConstructor = (*env)->GetMethodID(env, mainClass, "<init>", "()V"); |
| if(mainConstructor != NULL) { |
| mainObject = (*env)->NewObject(env, mainClass, mainConstructor); |
| if(mainObject != NULL) { |
| runMethod = (*env)->GetMethodID(env, mainClass, "run", "([Ljava/lang/String;)I"); |
| if(runMethod != NULL) { |
| methodArgs = createRunArgs(env, progArgs); |
| if(methodArgs != NULL) { |
| results->launchResult = 0; |
| results->runResult = (*env)->CallIntMethod(env, mainObject, runMethod, methodArgs); |
| (*env)->DeleteLocalRef(env, methodArgs); |
| } |
| } |
| (*env)->DeleteLocalRef(env, mainObject); |
| } |
| } |
| } else { |
| results->launchResult = -5; |
| results->errorMessage = malloc((_tcslen(mainClassNotFound) + _tcslen(jarFile) + 1) * sizeof(_TCHAR)); |
| _stprintf(results->errorMessage, mainClassNotFound, jarFile); |
| } |
| if((*env)->ExceptionOccurred(env)){ |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| } else { |
| results->launchResult = -4; |
| results->errorMessage = _tcsdup(failedCreateVM); |
| } |
| |
| /* toNarrow allocated new strings, free them */ |
| for(i = 0; i < numVMArgs; i++){ |
| free( options[i].optionString ); |
| } |
| free(options); |
| return results; |
| } |
| |
| static char * getMainClass(JNIEnv *env, _TCHAR * jarFile) { |
| jclass jarFileClass = NULL, manifestClass = NULL, attributesClass = NULL; |
| jmethodID jarFileConstructor = NULL, getManifestMethod = NULL, getMainAttributesMethod = NULL, closeJarMethod = NULL, getValueMethod = NULL; |
| jobject jarFileObject, manifest, attributes; |
| jstring mainClassString = NULL; |
| jstring jarFileString, headerString; |
| const _TCHAR *mainClass; |
| |
| /* get the classes we need */ |
| jarFileClass = (*env)->FindClass(env, "java/util/jar/JarFile"); |
| if (jarFileClass != NULL) { |
| manifestClass = (*env)->FindClass(env, "java/util/jar/Manifest"); |
| if (manifestClass != NULL) { |
| attributesClass = (*env)->FindClass(env, "java/util/jar/Attributes"); |
| } |
| } |
| if ((*env)->ExceptionOccurred(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| if (attributesClass == NULL) |
| return NULL; |
| |
| /* find the methods */ |
| jarFileConstructor = (*env)->GetMethodID(env, jarFileClass, "<init>", "(Ljava/lang/String;Z)V"); |
| if(jarFileConstructor != NULL) { |
| getManifestMethod = (*env)->GetMethodID(env, jarFileClass, "getManifest", "()Ljava/util/jar/Manifest;"); |
| if(getManifestMethod != NULL) { |
| closeJarMethod = (*env)->GetMethodID(env, jarFileClass, "close", "()V"); |
| if (closeJarMethod != NULL) { |
| getMainAttributesMethod = (*env)->GetMethodID(env, manifestClass, "getMainAttributes", "()Ljava/util/jar/Attributes;"); |
| if (getMainAttributesMethod != NULL) { |
| getValueMethod = (*env)->GetMethodID(env, attributesClass, "getValue", "(Ljava/lang/String;)Ljava/lang/String;"); |
| } |
| } |
| } |
| } |
| if ((*env)->ExceptionOccurred(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| if (getValueMethod == NULL) |
| return NULL; |
| |
| /* jarFileString = new String(jarFile); */ |
| jarFileString = newJavaString(env, jarFile); |
| /* headerString = new String("Main-Class"); */ |
| headerString = newJavaString(env, _T_ECLIPSE("Main-Class")); |
| if (jarFileString != NULL && headerString != NULL) { |
| /* jarfileObject = new JarFile(jarFileString, false); */ |
| jarFileObject = (*env)->NewObject(env, jarFileClass, jarFileConstructor, jarFileString, JNI_FALSE); |
| if (jarFileObject != NULL) { |
| /* manifest = jarFileObject.getManifest(); */ |
| manifest = (*env)->CallObjectMethod(env, jarFileObject, getManifestMethod); |
| if (manifest != NULL) { |
| /*jarFileObject.close() */ |
| (*env)->CallVoidMethod(env, jarFileObject, closeJarMethod); |
| if (!(*env)->ExceptionOccurred(env)) { |
| /* attributes = manifest.getMainAttributes(); */ |
| attributes = (*env)->CallObjectMethod(env, manifest, getMainAttributesMethod); |
| if (attributes != NULL) { |
| /* mainClassString = attributes.getValue(headerString); */ |
| mainClassString = (*env)->CallObjectMethod(env, attributes, getValueMethod, headerString); |
| } |
| } |
| } |
| (*env)->DeleteLocalRef(env, jarFileObject); |
| } |
| } |
| |
| if (jarFileString != NULL) |
| (*env)->DeleteLocalRef(env, jarFileString); |
| if (headerString != NULL) |
| (*env)->DeleteLocalRef(env, headerString); |
| |
| if ((*env)->ExceptionOccurred(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| if (mainClassString == NULL) |
| return NULL; |
| |
| mainClass = JNI_GetStringChars(env, mainClassString); |
| if(mainClass != NULL) { |
| int i = -1; |
| char *result = toNarrow(mainClass); |
| JNI_ReleaseStringChars(env, mainClassString, mainClass); |
| |
| /* replace all the '.' with '/' */ |
| while(result[++i] != '\0') { |
| if(result[i] == '.') |
| result[i] = '/'; |
| } |
| return result; |
| } |
| return NULL; |
| } |
| |
| void cleanupVM(int exitCode) { |
| JNIEnv * localEnv = env; |
| if (jvm == 0) |
| return; |
| |
| if (secondThread) |
| (*jvm)->AttachCurrentThread(jvm, (void**)&localEnv, NULL); |
| else |
| localEnv = env; |
| if (localEnv == 0) |
| return; |
| |
| /* we call System.exit() unless osgi.noShutdown is set */ |
| if (shouldShutdown(env)) { |
| jclass systemClass = NULL; |
| jmethodID exitMethod = NULL; |
| systemClass = (*env)->FindClass(env, "java/lang/System"); |
| if (systemClass != NULL) { |
| exitMethod = (*env)->GetStaticMethodID(env, systemClass, "exit", "(I)V"); |
| if (exitMethod != NULL) { |
| (*env)->CallStaticVoidMethod(env, systemClass, exitMethod, exitCode); |
| } |
| } |
| if ((*env)->ExceptionOccurred(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| (*jvm)->DestroyJavaVM(jvm); |
| } |
| |
| static int shouldShutdown(JNIEnv * env) { |
| jclass booleanClass = NULL; |
| jmethodID method = NULL; |
| jstring arg = NULL; |
| jboolean result = 0; |
| |
| booleanClass = (*env)->FindClass(env, "java/lang/Boolean"); |
| if (booleanClass != NULL) { |
| method = (*env)->GetStaticMethodID(env, booleanClass, "getBoolean", "(Ljava/lang/String;)Z"); |
| if (method != NULL) { |
| arg = newJavaString(env, _T_ECLIPSE("osgi.noShutdown")); |
| result = (*env)->CallStaticBooleanMethod(env, booleanClass, method, arg); |
| (*env)->DeleteLocalRef(env, arg); |
| } |
| } |
| if ((*env)->ExceptionOccurred(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| return (result == 0); |
| } |
| |
| |