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 |
|