4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/A#import "JVMArgs.h"
4632N/A
4632N/A
4632N/A#define kArgsFailure "JVMArgsFailure"
4632N/A
4632N/ANSString *kArgumentsKey = @"Arguments";
4632N/A
4632N/ANSString *kClassPathKey = @"ClassPath";
4632N/A#ifdef __i386__
4632N/ANSString *kArchClassPathKey = @"ClassPath.i386";
4632N/A#elif __x86_64__
4632N/ANSString *kArchClassPathKey = @"ClassPath.x86_64";
4632N/A#endif
4632N/A
4632N/ANSString *kVMOptionsKey = @"VMOptions";
4632N/A#ifdef __i386__
4632N/ANSString *kArchVMOptionsKey = @"VMOptions.i386";
4632N/A#elif __x86_64__
4632N/ANSString *kArchVMOptionsKey = @"VMOptions.x86_64";
4632N/A#endif
4632N/A
4632N/A
4632N/A@implementation JVMArgs
4632N/A
4632N/A@synthesize jreBundle;
4632N/A@synthesize preferredJVMLib;
4632N/A@synthesize vm_args;
4632N/A@synthesize startOnFirstThread;
4632N/A@synthesize debug;
4632N/A
4632N/A@synthesize appInfo;
4632N/A@synthesize jvmInfo;
4632N/A
4632N/A@synthesize userHome;
4632N/A@synthesize appPackage;
4632N/A@synthesize javaRoot;
4632N/A
4632N/A- (void) dealloc {
4632N/A self.jreBundle = nil;
4632N/A if (self.preferredJVMLib) free(self.preferredJVMLib);
4632N/A
4632N/A self.appInfo = nil;
4632N/A self.jvmInfo = nil;
4632N/A
4632N/A self.userHome = nil;
4632N/A self.appPackage = nil;
4632N/A self.javaRoot = nil;
4632N/A
4632N/A [super dealloc];
4632N/A}
4632N/A
4632N/A
4632N/ANSString *GetJavaRoot(NSDictionary *jvmInfoDict) {
4632N/A NSObject *javaRoot = [jvmInfoDict objectForKey:@"$JAVAROOT"];
4632N/A if (![javaRoot isKindOfClass:[NSString class]]) return @"$APP_PACKAGE/Contents/Java";
4632N/A return (NSString *)javaRoot;
4632N/A}
4632N/A
4632N/A// Replaces occurances of $JAVAROOT, $APP_PACKAGE, and $USER_HOME
4632N/A- (NSString *) expandMacros:(NSString *)str {
4632N/A if ([str rangeOfString:@"$JAVAROOT"].length == 0 && [str rangeOfString:@"$APP_PACKAGE"].length == 0 && [str rangeOfString:@"$USER_HOME"].length == 0) return str;
4632N/A
4632N/A // expand $JAVAROOT first, because it can contain $APP_PACKAGE
4632N/A NSMutableString *mutable = [str mutableCopy];
4632N/A [mutable replaceOccurrencesOfString:@"$JAVAROOT" withString:javaRoot options:0 range:NSMakeRange(0, [str length])];
4632N/A [mutable replaceOccurrencesOfString:@"$APP_PACKAGE" withString:appPackage options:0 range:NSMakeRange(0, [str length])];
4632N/A [mutable replaceOccurrencesOfString:@"$USER_HOME" withString:userHome options:0 range:NSMakeRange(0, [str length])];
4632N/A return mutable;
4632N/A}
4632N/A
4632N/A- (NSArray *) arrayFrom:(id) obj delimitedBy:(NSString *)delimiter withErrKey:(NSString *)key {
4632N/A if (obj == nil) return nil;
4632N/A if ([obj isKindOfClass:[NSArray class]]) return obj;
4632N/A if (![obj isKindOfClass:[NSString class]]) {
4632N/A [NSException raise:@kArgsFailure format:@"%@", [NSString stringWithFormat:@"Failed to find '%@' array in JVMInfo Info.plist"]];
4632N/A }
4632N/A
4632N/A // split
4632N/A return [(NSString *)obj componentsSeparatedByString:delimiter];
4632N/A}
4632N/A
4632N/A- (void) buildArgsForBundle:(NSBundle *)appBundle argc:(int)argc argv:(char *[])argv {
4632N/A // for verbose logging
4632N/A self.debug = NULL != getenv("JAVA_LAUNCHER_VERBOSE");
4632N/A
4632N/A self.appInfo = [appBundle infoDictionary];
4632N/A
4632N/A // all apps must have a JVMInfo dictionary inside their Info.plist
4632N/A self.jvmInfo = [[self.appInfo objectForKey:@"JVMInfo"] mutableCopy];
4632N/A if (![jvmInfo isKindOfClass:[NSDictionary class]]) {
4632N/A [NSException raise:@kArgsFailure format:@"Failed to find 'JVMInfo' dictionary in Info.plist"];
4632N/A }
4632N/A
4632N/A // initialize macro expansion values
4632N/A self.userHome = NSHomeDirectory();
4632N/A self.appPackage = [appBundle bundlePath];
4632N/A self.javaRoot = GetJavaRoot(jvmInfo);
4632N/A self.javaRoot = [self expandMacros:self.javaRoot]; // dereference $APP_PACKAGE
4632N/A
4632N/A // if the 'Arguments' key is defined, those override the ones that came into main()
4632N/A NSArray *jvmInfoArgs = [jvmInfo valueForKey:kArgumentsKey];
4632N/A if (jvmInfoArgs != nil) {
4632N/A // substitute all the variables in the 'Arguments' array/string
4632N/A jvmInfoArgs = [self arrayFrom:jvmInfoArgs delimitedBy:@" " withErrKey:kArgumentsKey];
4632N/A NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:[jvmInfoArgs count]];
4632N/A [jvmInfoArgs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
4632N/A [arguments replaceObjectAtIndex:idx withObject:[self expandMacros:[obj description]]];
4632N/A }];
4632N/A [jvmInfo setObject:arguments forKey:kArgumentsKey];
4632N/A } else if (argc != 0) {
4632N/A // put the (macro expanded) args to main() in an NSArray
4632N/A NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:argc];
4632N/A for (int i = 0; i < argc; i++) {
4632N/A [arguments addObject:[self expandMacros:[NSString stringWithUTF8String:(argv[i])]]];
4632N/A }
4632N/A [jvmInfo setObject:arguments forKey:kArgumentsKey];
4632N/A }
4632N/A
4632N/A // all JVMInfo's must have a JRE or JDK key
4632N/A NSString *jreBundleName = [jvmInfo objectForKey:@"JRE"];
4632N/A if (!jreBundleName) jreBundleName = [jvmInfo objectForKey:@"JDK"];
4632N/A if (![jreBundleName isKindOfClass:[NSString class]]) {
4632N/A [NSException raise:@kArgsFailure format:@"Failed to find 'JRE' or 'JDK' string in Info.plist JVMInfo"];
4632N/A }
4632N/A
4632N/A // the JRE/JDK must be loadable from the ($APP_PACKAGE)/Contents/PlugIns/ directory
4632N/A NSURL *jreBundleURL = [[appBundle builtInPlugInsURL] URLByAppendingPathComponent:jreBundleName];
4632N/A self.jreBundle = [NSBundle bundleWithURL:jreBundleURL];
4632N/A if (!self.jreBundle) {
4632N/A [NSException raise:@kArgsFailure format:@"Failed to find JRE/JDK at: %@", jreBundleURL];
4632N/A }
4632N/A
4632N/A // if the app prefers 'client' or 'server', use the JVM key
4632N/A NSString *JVMLib = [jvmInfo objectForKey:@"JVM"];
4632N/A if (JVMLib != nil) self.preferredJVMLib = strdup([JVMLib UTF8String]);
4632N/A
4632N/A // sniff for StartOnFirstThread
4632N/A if ([[jvmInfo objectForKey:@"StartOnFirstThread"] boolValue]) {
4632N/A self.startOnFirstThread = YES;
4632N/A } else if ([[jvmInfo objectForKey:@"StartOnMainThread"] boolValue]) {
4632N/A // for key compatability with the Apple JavaApplicationStub's 'Java' dictionary
4632N/A self.startOnFirstThread = YES;
4632N/A }
4632N/A
4632N/A // add $JAVAROOT directory to the JNI library search path
4632N/A setenv("JAVA_LIBRARY_PATH", [javaRoot UTF8String], 1);
4632N/A
4632N/A // 'WorkingDirectory' key changes current working directory
4632N/A NSString *javaWorkingDir = [jvmInfo objectForKey:@"WorkingDirectory"];
4632N/A if (javaWorkingDir == nil) javaWorkingDir = @"$APP_PACKAGE/..";
4632N/A javaWorkingDir = [self expandMacros:javaWorkingDir];
4632N/A if (chdir([javaWorkingDir UTF8String]) == -1) {
4632N/A NSLog(@kArgsFailure " chdir() failed, could not change the current working directory to %s\n", [javaWorkingDir UTF8String]);
4632N/A }
4632N/A
4632N/A NSMutableArray *classpath = [NSMutableArray array];
4632N/A
4632N/A // 'Jar' key sets exactly one classpath entry
4632N/A NSString *jarFile = [jvmInfo objectForKey:@"Jar"];
4632N/A if (jarFile != nil) {
4632N/A [jvmInfo setObject:[self expandMacros:jarFile] forKey:@"Jar"];
4632N/A [classpath addObject:jarFile];
4632N/A }
4632N/A
4632N/A // 'ClassPath' key allows arbitrary classpath
4632N/A [classpath addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kClassPathKey] delimitedBy:@":" withErrKey:kClassPathKey]];
4632N/A [classpath addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kArchClassPathKey] delimitedBy:@":" withErrKey:kArchClassPathKey]];
4632N/A
4632N/A // Sum up all the classpath entries into one big JVM arg
4632N/A NSMutableString *classpathOption = [NSMutableString stringWithString:@"-Djava.class.path="];
4632N/A [classpath enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
4632N/A if (idx > 1) [classpathOption appendString:@":"];
4632N/A [classpathOption appendString:obj];
4632N/A }];
4632N/A
4632N/A NSMutableArray *jvmOptions = [NSMutableArray arrayWithObject:classpathOption];
4632N/A
4632N/A // 'VMOptions' key allows arbitary VM start up options
4632N/A [jvmOptions addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kVMOptionsKey] delimitedBy:@" " withErrKey:kVMOptionsKey]];
4632N/A [jvmOptions addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kArchVMOptionsKey] delimitedBy:@" " withErrKey:kArchVMOptionsKey]];
4632N/A
4632N/A // 'Properties' key is a sub-dictionary transfered to initial System.properties
4632N/A NSDictionary *properties = [jvmInfo objectForKey:@"Properties"];
4632N/A if (properties != nil) {
4632N/A if (![properties isKindOfClass:[NSDictionary class]]) {
4632N/A [NSException raise:@kArgsFailure format:@"Failed to find 'Properties' dictionary in Info.plist JVMInfo"];
4632N/A }
4632N/A
4632N/A [properties enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
4632N/A [jvmOptions addObject:[NSString stringWithFormat:@"-D%@=%@", key, obj]];
4632N/A }];
4632N/A }
4632N/A
4632N/A // build the real JVM init args struct
4632N/A vm_args.version = JNI_VERSION_1_6;
4632N/A vm_args.ignoreUnrecognized = JNI_TRUE;
4632N/A vm_args.nOptions = [jvmOptions count];
4632N/A vm_args.options = calloc(vm_args.nOptions, sizeof(JavaVMOption));
4632N/A [jvmOptions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
4632N/A NSString *expanded = [self expandMacros:[obj description]]; // turn everything into a string, and expand macros
4632N/A vm_args.options[idx].optionString = strdup([expanded UTF8String]);
4632N/A }];
4632N/A}
4632N/A
4632N/A+ (JVMArgs *)jvmArgsForBundle:(NSBundle *)appBundle argc:(int)argc argv:(char *[])argv {
4632N/A JVMArgs *args = [JVMArgs new];
4632N/A [args buildArgsForBundle:appBundle argc:argc argv:argv];
4632N/A return [args autorelease];
4632N/A}
4632N/A
4632N/A@end