1 /*
   2  * Copyright 2012, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 
  27 #import <Cocoa/Cocoa.h>
  28 #include <jni.h>
  29 
  30 #define JAVA_LAUNCH_ERROR "JavaLaunchError"
  31 
  32 #define JAVA_VM_KEY "JavaVM"
  33 #define RUNTIME_KEY "Runtime"
  34 #define MAIN_CLASS_NAME_KEY "MainClassName"
  35 #define OPTIONS_KEY "Options"
  36 #define ARGUMENTS_KEY "Arguments"
  37 
  38 // TODO Remove these; they are defined by the makefile
  39 #define FULL_VERSION "1.7.0"
  40 #define DOT_VERSION "1.7.0"
  41 #define DEFAULT_POLICY 0
  42 
  43 typedef int (JNICALL *JLI_Launch_t)(int argc, char ** argv,
  44                                     int jargc, const char** jargv,
  45                                     int appclassc, const char** appclassv,
  46                                     const char* fullversion,
  47                                     const char* dotversion,
  48                                     const char* pname,
  49                                     const char* lname,
  50                                     jboolean javaargs,
  51                                     jboolean cpwildcard,
  52                                     jboolean javaw,
  53                                     jint ergo);
  54 
  55 int launch(char *);
  56 int jli_launch(char *, NSURL *, NSString *, NSString *, NSString *, NSArray *, NSArray *);
  57 
  58 int main(int argc, char *argv[]) {
  59     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  60 
  61     int result;
  62     @try {
  63         launch(argv[0]);
  64         result = 0;
  65     } @catch (NSException *exception) {
  66         NSLog(@"%@: %@", exception, [exception callStackSymbols]);
  67         result = 1;
  68     }
  69 
  70     [pool drain];
  71 
  72     return result;
  73 }
  74 
  75 int launch(char *commandName) {
  76     // Get the main bundle
  77     NSBundle *mainBundle = [NSBundle mainBundle];
  78     NSDictionary *infoDictionary = [mainBundle infoDictionary];
  79 
  80     NSString *javaVMPath = [[mainBundle bundlePath] stringByAppendingString:@"/Contents/JavaVM"];
  81 
  82     // Get the Java dictionary
  83     NSDictionary *javaVMDictionary = [infoDictionary objectForKey:@JAVA_VM_KEY];
  84     if (javaVMDictionary == nil) {
  85         [NSException raise:@JAVA_LAUNCH_ERROR format:@"%@ is required.", @JAVA_VM_KEY];
  86     }
  87 
  88     // Get the runtime bundle URL
  89     NSString *runtime = [javaVMDictionary objectForKey:@RUNTIME_KEY];
  90 
  91     // TODO If unspecified, use default runtime location
  92 
  93     NSURL *runtimeBundleURL = [[mainBundle builtInPlugInsURL] URLByAppendingPathComponent:runtime];
  94 
  95     // Get the main class name
  96     NSString *mainClassName = [javaVMDictionary objectForKey:@MAIN_CLASS_NAME_KEY];
  97     if (mainClassName == nil) {
  98         [NSException raise:@JAVA_LAUNCH_ERROR format:@"%@ is required.", @MAIN_CLASS_NAME_KEY];
  99     }
 100 
 101     // Set the class path
 102     NSString *classPathFormat = @"-Djava.class.path=%@/Classes";
 103     NSMutableString *classPath = [[NSString stringWithFormat:classPathFormat, javaVMPath] mutableCopy];
 104 
 105     NSFileManager *defaultFileManager = [NSFileManager defaultManager];
 106     NSArray *javaDirectoryContents = [defaultFileManager contentsOfDirectoryAtPath:javaVMPath error:nil];
 107     if (javaDirectoryContents == nil) {
 108         [NSException raise:@JAVA_LAUNCH_ERROR format:@"Could not enumerate Java directory contents."];
 109     }
 110 
 111     for (NSString *file in javaDirectoryContents) {
 112         if ([file hasSuffix:@".jar"]) {
 113             [classPath appendFormat:@":%@/%@", javaVMPath, file];
 114         }
 115     }
 116 
 117     // Set the library path
 118     NSString *libraryPathFormat = @"-Djava.library.path=%@";
 119     NSString *libraryPath = [NSString stringWithFormat:libraryPathFormat, javaVMPath];
 120 
 121     // Get the VM options
 122     NSArray *options = [javaVMDictionary objectForKey:@OPTIONS_KEY];
 123     if (options == nil) {
 124         options = [NSArray array];
 125     }
 126 
 127     // Get the application arguments
 128     NSArray *arguments = [javaVMDictionary objectForKey:@ARGUMENTS_KEY];
 129     if (arguments == nil) {
 130         arguments = [NSArray array];
 131     }
 132 
 133     return jli_launch(commandName, runtimeBundleURL,
 134                       mainClassName, classPath, libraryPath,
 135                       options, arguments);
 136 }
 137 
 138 int jli_launch(char *commandName, NSURL *runtimeBundleURL,
 139                NSString *mainClassName, NSString *classPath, NSString *libraryPath,
 140                NSArray *options, NSArray *arguments) {
 141     // Load the runtime bundle
 142     CFBundleRef runtimeBundle = CFBundleCreate(NULL, (CFURLRef)runtimeBundleURL);
 143 
 144     NSError *bundleLoadError = nil;
 145     Boolean runtimeBundleLoaded = CFBundleLoadExecutableAndReturnError(runtimeBundle, (CFErrorRef *)&bundleLoadError);
 146     if (bundleLoadError != nil || !runtimeBundleLoaded) {
 147         [NSException raise:@JAVA_LAUNCH_ERROR format:@"Could not load JRE from %@.", bundleLoadError];
 148     }
 149 
 150     // Get the JLI_Launch() function pointer
 151     JLI_Launch_t JLI_LaunchFxnPtr = CFBundleGetFunctionPointerForName(runtimeBundle, CFSTR("JLI_Launch"));
 152     if (JLI_LaunchFxnPtr == NULL) {
 153         [NSException raise:@JAVA_LAUNCH_ERROR format:@"Could not get function pointer for JLI_Launch."];
 154     }
 155 
 156     // Initialize the arguments to JLI_Launch()
 157     int argc = 1 + [options count] + 2 + [arguments count] + 1;
 158     char *argv[argc];
 159 
 160     int i = 0;
 161     argv[i++] = commandName;
 162     argv[i++] = strdup([classPath UTF8String]);
 163     argv[i++] = strdup([libraryPath UTF8String]);
 164 
 165     for (NSString *option in options) {
 166         argv[i++] = strdup([option UTF8String]);
 167     }
 168 
 169     argv[i++] = strdup([mainClassName UTF8String]);
 170 
 171     for (NSString *argument in arguments) {
 172         argv[i++] = strdup([argument UTF8String]);
 173     }
 174 
 175     // Invoke JLI_Launch()
 176     return JLI_LaunchFxnPtr(argc, argv,
 177                             0, NULL,
 178                             0, NULL,
 179                             FULL_VERSION,
 180                             DOT_VERSION,
 181                             "java",
 182                             "java",
 183                             FALSE,
 184                             FALSE,
 185                             FALSE,
 186                             DEFAULT_POLICY);
 187 }