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