1 /* 2 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import "JavaAppLauncher.h" 27 28 #import <dlfcn.h> 29 30 #import "jni.h" 31 32 #define kLaunchFailure "JavaAppLauncherFailure" 33 34 35 typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args); 36 typedef void (JNICALL *SetPreferredJVM_t)(const char *prefJVM); 37 38 39 @implementation JavaAppLauncher 40 41 @synthesize args; 42 43 - (void) findAndLoadJVM { 44 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 45 46 @try { 47 // load the libjli.dylib of the embedded JRE (or JDK) bundle 48 NSURL *jreBundleURL = [args.jreBundle bundleURL]; 49 CFBundleRef jreBundle = CFBundleCreate(NULL, (CFURLRef)jreBundleURL); 50 51 NSError *err = nil; 52 Boolean jreBundleLoaded = CFBundleLoadExecutableAndReturnError(jreBundle, (CFErrorRef *)&err); 53 if (err != nil || !jreBundleLoaded) { 54 [NSException raise:@kLaunchFailure format:@"could not load the JRE/JDK: %@", err]; 55 } 56 57 // if there is a preferred libjvm to load, set it here 58 if (args.preferredJVMLib != NULL) { 59 SetPreferredJVM_t setPrefJVMFxnPtr = CFBundleGetFunctionPointerForName(jreBundle, CFSTR("JLI_SetPreferredJVM")); 60 if (setPrefJVMFxnPtr != NULL) { 61 setPrefJVMFxnPtr(args.preferredJVMLib); 62 } else { 63 NSLog(@"No JLI_SetPreferredJVM in JRE/JDK primary executable, failed to set preferred JVM library to: %s", args->preferredJVMLib); 64 } 65 } 66 67 // pull the JNI_CreateJavaVM function pointer out of the primary executable of the JRE/JDK bundle 68 CreateJavaVM_t createJVMFxnPtr = CFBundleGetFunctionPointerForName(jreBundle, CFSTR("JNI_CreateJavaVM")); 69 if (createJVMFxnPtr == NULL) { 70 [NSException raise:@kLaunchFailure format:@"null JNI_CreateJavaVM fxn ptr from: %@", jreBundle]; 71 } 72 73 // instantiate the JVM 74 JNIEnv *env; 75 jint createJVMStatus = createJVMFxnPtr(&jvm, (void **)&env, &(args->vm_args)); 76 if (createJVMStatus != JNI_OK) { 77 [NSException raise:@kLaunchFailure format:@"failed to JNI_CreateJavaVM (%d): %@", createJVMStatus, jreBundle]; 78 } 79 80 // check the app needs to run the Java main() on the main thread 81 if (args.startOnFirstThread) { 82 dispatch_sync(dispatch_get_main_queue(), ^(void) { 83 JNIEnv *mainThreadEnv; 84 (*jvm)->AttachCurrentThread(jvm, (void **)&mainThreadEnv, NULL); 85 [self invokeBundledAppJavaLauncherWithEnv:mainThreadEnv]; 86 (*jvm)->DetachCurrentThread(jvm); 87 }); 88 } else { 89 [self invokeBundledAppJavaLauncherWithEnv:env]; 90 } 91 92 } @catch (NSException *e) { 93 NSLog(@"%@: %@", e, [e callStackSymbols]); 94 } 95 96 if (jvm) { 97 (*jvm)->DetachCurrentThread(jvm); 98 (*jvm)->DestroyJavaVM(jvm); 99 } 100 101 [pool drain]; 102 } 103 104 static const char kLauncherClassName[] = "apple/launcher/JavaAppLauncher"; 105 106 - (void) invokeBundledAppJavaLauncherWithEnv:(JNIEnv *)env { 107 // hand off control to the apple.launcher.JavaAppLauncher class 108 109 jclass mainClass = (*env)->FindClass(env, kLauncherClassName); 110 if (mainClass == NULL) { 111 fprintf(stderr, kLaunchFailure " FindClass() failed for class %s:\n", kLauncherClassName); 112 (*env)->ExceptionDescribe(env); 113 return; 114 } 115 116 jmethodID mainMethod = (*env)->GetStaticMethodID(env, mainClass, "launch", "(JZ)V"); 117 if ((mainMethod == NULL) || (*env)->ExceptionOccurred(env)) { 118 fprintf(stderr, kLaunchFailure " GetStaticMethodID() failed for launch() method"); 119 (*env)->ExceptionDescribe(env); 120 return; 121 } 122 123 CFDictionaryRef jvmInfo = CFRetain(args.jvmInfo); 124 125 (*env)->CallStaticVoidMethod(env, mainClass, mainMethod, (jlong)jvmInfo, (jboolean)args.debug); 126 if ((*env)->ExceptionOccurred(env)) { 127 fprintf(stderr, kLaunchFailure " CallStaticVoidMethod() threw an exception\n"); 128 (*env)->ExceptionDescribe(env); 129 return; 130 } 131 } 132 133 @end