jdk/src/macosx/bundle/JavaAppLauncher/src/JVMArgs.m
changeset 25842 f322f27b47d2
parent 25841 f51938efd4f6
parent 25838 02b8ba539ccb
child 25850 c3d78c30da05
equal deleted inserted replaced
25841:f51938efd4f6 25842:f322f27b47d2
     1 /*
       
     2  * Copyright (c) 2011, 2013, 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 "JVMArgs.h"
       
    27 
       
    28 
       
    29 #define kArgsFailure "JVMArgsFailure"
       
    30 
       
    31 NSString *kArgumentsKey = @"Arguments";
       
    32 
       
    33 NSString *kClassPathKey = @"ClassPath";
       
    34 #ifdef __i386__
       
    35 NSString *kArchClassPathKey = @"ClassPath.i386";
       
    36 #elif __x86_64__
       
    37 NSString *kArchClassPathKey = @"ClassPath.x86_64";
       
    38 #endif
       
    39 
       
    40 NSString *kVMOptionsKey = @"VMOptions";
       
    41 #ifdef __i386__
       
    42 NSString *kArchVMOptionsKey = @"VMOptions.i386";
       
    43 #elif __x86_64__
       
    44 NSString *kArchVMOptionsKey = @"VMOptions.x86_64";
       
    45 #endif
       
    46 
       
    47 
       
    48 @implementation JVMArgs
       
    49 
       
    50 @synthesize jreBundle;
       
    51 @synthesize preferredJVMLib;
       
    52 @synthesize vm_args;
       
    53 @synthesize startOnFirstThread;
       
    54 @synthesize debug;
       
    55 
       
    56 @synthesize appInfo;
       
    57 @synthesize jvmInfo;
       
    58 
       
    59 @synthesize userHome;
       
    60 @synthesize appPackage;
       
    61 @synthesize javaRoot;
       
    62 
       
    63 - (void) dealloc {
       
    64     self.jreBundle = nil;
       
    65     if (self.preferredJVMLib) free(self.preferredJVMLib);
       
    66 
       
    67     self.appInfo = nil;
       
    68     self.jvmInfo = nil;
       
    69 
       
    70     self.userHome = nil;
       
    71     self.appPackage = nil;
       
    72     self.javaRoot = nil;
       
    73 
       
    74     [super dealloc];
       
    75 }
       
    76 
       
    77 
       
    78 NSString *GetJavaRoot(NSDictionary *jvmInfoDict) {
       
    79     NSObject *javaRoot = [jvmInfoDict objectForKey:@"$JAVAROOT"];
       
    80     if (![javaRoot isKindOfClass:[NSString class]]) return @"$APP_PACKAGE/Contents/Java";
       
    81     return (NSString *)javaRoot;
       
    82 }
       
    83 
       
    84 // Replaces occurances of $JAVAROOT, $APP_PACKAGE, and $USER_HOME
       
    85 - (NSString *) expandMacros:(NSString *)str {
       
    86     if ([str rangeOfString:@"$JAVAROOT"].length == 0 && [str rangeOfString:@"$APP_PACKAGE"].length == 0 && [str rangeOfString:@"$USER_HOME"].length == 0) return str;
       
    87 
       
    88     // expand $JAVAROOT first, because it can contain $APP_PACKAGE
       
    89     NSMutableString *mutable = [str mutableCopy];
       
    90     [mutable replaceOccurrencesOfString:@"$JAVAROOT" withString:javaRoot options:0 range:NSMakeRange(0, [str length])];
       
    91     [mutable replaceOccurrencesOfString:@"$APP_PACKAGE" withString:appPackage options:0 range:NSMakeRange(0, [str length])];
       
    92     [mutable replaceOccurrencesOfString:@"$USER_HOME" withString:userHome options:0 range:NSMakeRange(0, [str length])];
       
    93     return mutable;
       
    94 }
       
    95 
       
    96 - (NSArray *) arrayFrom:(id) obj delimitedBy:(NSString *)delimiter withErrKey:(NSString *)key {
       
    97     if (obj == nil) return nil;
       
    98     if ([obj isKindOfClass:[NSArray class]]) return obj;
       
    99     if (![obj isKindOfClass:[NSString class]]) {
       
   100         [NSException raise:@kArgsFailure format:@"%@", [NSString stringWithFormat:@"Failed to find '%@' array in JVMInfo Info.plist"]];
       
   101     }
       
   102 
       
   103     // split
       
   104     return [(NSString *)obj componentsSeparatedByString:delimiter];
       
   105 }
       
   106 
       
   107 - (void) buildArgsForBundle:(NSBundle *)appBundle argc:(int)argc argv:(char *[])argv {
       
   108     // for verbose logging
       
   109     self.debug = NULL != getenv("JAVA_LAUNCHER_VERBOSE");
       
   110 
       
   111     self.appInfo = [appBundle infoDictionary];
       
   112 
       
   113     // all apps must have a JVMInfo dictionary inside their Info.plist
       
   114     self.jvmInfo = [[self.appInfo objectForKey:@"JVMInfo"] mutableCopy];
       
   115     if (![jvmInfo isKindOfClass:[NSDictionary class]]) {
       
   116         [NSException raise:@kArgsFailure format:@"Failed to find 'JVMInfo' dictionary in Info.plist"];
       
   117     }
       
   118 
       
   119     // initialize macro expansion values
       
   120     self.userHome = NSHomeDirectory();
       
   121     self.appPackage = [appBundle bundlePath];
       
   122     self.javaRoot = GetJavaRoot(jvmInfo);
       
   123     self.javaRoot = [self expandMacros:self.javaRoot]; // dereference $APP_PACKAGE
       
   124 
       
   125     // if the 'Arguments' key is defined, those override the ones that came into main()
       
   126     NSArray *jvmInfoArgs = [jvmInfo valueForKey:kArgumentsKey];
       
   127     if (jvmInfoArgs != nil) {
       
   128         // substitute all the variables in the 'Arguments' array/string
       
   129         jvmInfoArgs = [self arrayFrom:jvmInfoArgs delimitedBy:@" " withErrKey:kArgumentsKey];
       
   130         NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:[jvmInfoArgs count]];
       
   131         [jvmInfoArgs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
       
   132             [arguments replaceObjectAtIndex:idx withObject:[self expandMacros:[obj description]]];
       
   133         }];
       
   134         [jvmInfo setObject:arguments forKey:kArgumentsKey];
       
   135     } else if (argc != 0) {
       
   136         // put the (macro expanded) args to main() in an NSArray
       
   137         NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:argc];
       
   138         for (int i = 0; i < argc; i++) {
       
   139             [arguments addObject:[self expandMacros:[NSString stringWithUTF8String:(argv[i])]]];
       
   140         }
       
   141         [jvmInfo setObject:arguments forKey:kArgumentsKey];
       
   142     }
       
   143 
       
   144     // all JVMInfo's must have a JRE or JDK key
       
   145     NSString *jreBundleName = [jvmInfo objectForKey:@"JRE"];
       
   146     if (!jreBundleName) jreBundleName = [jvmInfo objectForKey:@"JDK"];
       
   147     if (![jreBundleName isKindOfClass:[NSString class]]) {
       
   148         [NSException raise:@kArgsFailure format:@"Failed to find 'JRE' or 'JDK' string in Info.plist JVMInfo"];
       
   149     }
       
   150 
       
   151     // the JRE/JDK must be loadable from the ($APP_PACKAGE)/Contents/PlugIns/ directory
       
   152     NSURL *jreBundleURL = [[appBundle builtInPlugInsURL] URLByAppendingPathComponent:jreBundleName];
       
   153     self.jreBundle = [NSBundle bundleWithURL:jreBundleURL];
       
   154     if (!self.jreBundle) {
       
   155         [NSException raise:@kArgsFailure format:@"Failed to find JRE/JDK at: %@", jreBundleURL];
       
   156     }
       
   157 
       
   158     // if the app prefers 'client' or 'server', use the JVM key
       
   159     NSString *JVMLib = [jvmInfo objectForKey:@"JVM"];
       
   160     if (JVMLib != nil) self.preferredJVMLib = strdup([JVMLib UTF8String]);
       
   161 
       
   162     // sniff for StartOnFirstThread
       
   163     if ([[jvmInfo objectForKey:@"StartOnFirstThread"] boolValue]) {
       
   164         self.startOnFirstThread = YES;
       
   165     } else if ([[jvmInfo objectForKey:@"StartOnMainThread"] boolValue]) {
       
   166         // for key compatibility with the Apple JavaApplicationStub's 'Java' dictionary
       
   167         self.startOnFirstThread = YES;
       
   168     }
       
   169 
       
   170     // add $JAVAROOT directory to the JNI library search path
       
   171     setenv("JAVA_LIBRARY_PATH", [javaRoot UTF8String], 1);
       
   172 
       
   173     // 'WorkingDirectory' key changes current working directory
       
   174     NSString *javaWorkingDir = [jvmInfo objectForKey:@"WorkingDirectory"];
       
   175     if (javaWorkingDir == nil) javaWorkingDir = @"$APP_PACKAGE/..";
       
   176     javaWorkingDir = [self expandMacros:javaWorkingDir];
       
   177     if (chdir([javaWorkingDir UTF8String]) == -1) {
       
   178         NSLog(@kArgsFailure " chdir() failed, could not change the current working directory to %s\n", [javaWorkingDir UTF8String]);
       
   179     }
       
   180 
       
   181     NSMutableArray *classpath = [NSMutableArray array];
       
   182 
       
   183     // 'Jar' key sets exactly one classpath entry
       
   184     NSString *jarFile = [jvmInfo objectForKey:@"Jar"];
       
   185     if (jarFile != nil) {
       
   186         [jvmInfo setObject:[self expandMacros:jarFile] forKey:@"Jar"];
       
   187         [classpath addObject:jarFile];
       
   188     }
       
   189 
       
   190     // 'ClassPath' key allows arbitrary classpath
       
   191     [classpath addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kClassPathKey] delimitedBy:@":" withErrKey:kClassPathKey]];
       
   192     [classpath addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kArchClassPathKey] delimitedBy:@":" withErrKey:kArchClassPathKey]];
       
   193 
       
   194     // Sum up all the classpath entries into one big JVM arg
       
   195     NSMutableString *classpathOption = [NSMutableString stringWithString:@"-Djava.class.path="];
       
   196     [classpath enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
       
   197         if (idx > 1) [classpathOption appendString:@":"];
       
   198         [classpathOption appendString:obj];
       
   199     }];
       
   200 
       
   201     NSMutableArray *jvmOptions = [NSMutableArray arrayWithObject:classpathOption];
       
   202 
       
   203     // 'VMOptions' key allows arbitary VM start up options
       
   204     [jvmOptions addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kVMOptionsKey] delimitedBy:@" " withErrKey:kVMOptionsKey]];
       
   205     [jvmOptions addObjectsFromArray:[self arrayFrom:[jvmInfo objectForKey:kArchVMOptionsKey] delimitedBy:@" " withErrKey:kArchVMOptionsKey]];
       
   206 
       
   207     // 'Properties' key is a sub-dictionary transfered to initial System.properties
       
   208     NSDictionary *properties = [jvmInfo objectForKey:@"Properties"];
       
   209     if (properties != nil) {
       
   210         if (![properties isKindOfClass:[NSDictionary class]]) {
       
   211             [NSException raise:@kArgsFailure format:@"Failed to find 'Properties' dictionary in Info.plist JVMInfo"];
       
   212         }
       
   213 
       
   214         [properties enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
       
   215             [jvmOptions addObject:[NSString stringWithFormat:@"-D%@=%@", key, obj]];
       
   216         }];
       
   217     }
       
   218 
       
   219     // build the real JVM init args struct
       
   220     vm_args.version = JNI_VERSION_1_6;
       
   221     vm_args.ignoreUnrecognized = JNI_TRUE;
       
   222     vm_args.nOptions = [jvmOptions count];
       
   223     vm_args.options = calloc(vm_args.nOptions, sizeof(JavaVMOption));
       
   224     [jvmOptions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
       
   225         NSString *expanded = [self expandMacros:[obj description]]; // turn everything into a string, and expand macros
       
   226         vm_args.options[idx].optionString = strdup([expanded UTF8String]);
       
   227     }];
       
   228 }
       
   229 
       
   230 + (JVMArgs *)jvmArgsForBundle:(NSBundle *)appBundle argc:(int)argc argv:(char *[])argv {
       
   231     JVMArgs *args = [JVMArgs new];
       
   232     [args buildArgsForBundle:appBundle argc:argc argv:argv];
       
   233     return [args autorelease];
       
   234 }
       
   235 
       
   236 @end