1 /* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 34 #include "JavaVirtualMachine.h" 35 #include "Platform.h" 36 #include "PlatformString.h" 37 #include "FilePath.h" 38 #include "Package.h" 39 #include "Java.h" 40 #include "Helpers.h" 41 #include "Messages.h" 42 #include "Macros.h" 43 #include "PlatformThread.h" 44 45 #include "jni.h" 46 47 #include <map> 48 #include <list> 49 #include <sstream> 50 51 52 bool RunVM(JvmLaunchType type) { 53 bool result = false; 54 JavaVirtualMachine javavm; 55 56 switch (type){ 57 case USER_APP_LAUNCH: 58 result = javavm.StartJVM(); 59 break; 60 case SINGLE_INSTANCE_NOTIFICATION_LAUNCH: 61 result = javavm.NotifySingleInstance(); 62 break; 63 } 64 65 if (!result) { 66 Platform& platform = Platform::GetInstance(); 67 platform.ShowMessage(_T("Failed to launch JVM\n")); 68 } 69 70 return result; 71 } 72 73 JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL) { 74 } 75 76 bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) { 77 if (FCreateProc == NULL) { 78 FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC); 79 } 80 81 if (FCreateProc == NULL) { 82 Platform& platform = Platform::GetInstance(); 83 Messages& messages = Messages::GetInstance(); 84 platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); 85 return false; 86 } 87 88 return FCreateProc((int)argc, argv, 89 0, NULL, 90 0, NULL, 91 "", 92 "", 93 "java", 94 "java", 95 false, 96 false, 97 false, 98 0) == 0; 99 } 100 101 //-------------------------------------------------------------------------------------------------- 102 103 JavaOptions::JavaOptions(): FOptions(NULL) { 104 } 105 106 JavaOptions::~JavaOptions() { 107 if (FOptions != NULL) { 108 for (unsigned int index = 0; index < GetCount(); index++) { 109 delete[] FOptions[index].optionString; 110 } 111 112 delete[] FOptions; 113 } 114 } 115 116 void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) { 117 JavaOptionItem item; 118 item.name = Key; 119 item.value = Value; 120 item.extraInfo = Extra; 121 FItems.push_back(item); 122 } 123 124 void JavaOptions::AppendValue(const TString Key, TString Value) { 125 AppendValue(Key, Value, NULL); 126 } 127 128 void JavaOptions::AppendValue(const TString Key) { 129 AppendValue(Key, _T(""), NULL); 130 } 131 132 void JavaOptions::AppendValues(OrderedMap<TString, TString> Values) { 133 std::vector<TString> orderedKeys = Values.GetKeys(); 134 135 for (std::vector<TString>::const_iterator iterator = orderedKeys.begin(); 136 iterator != orderedKeys.end(); iterator++) { 137 TString name = *iterator; 138 TString value; 139 140 if (Values.GetValue(name, value) == true) { 141 AppendValue(name, value); 142 } 143 } 144 } 145 146 void JavaOptions::ReplaceValue(const TString Key, TString Value) { 147 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 148 iterator != FItems.end(); iterator++) { 149 150 TString lkey = iterator->name; 151 152 if (lkey == Key) { 153 JavaOptionItem item = *iterator; 154 item.value = Value; 155 iterator = FItems.erase(iterator); 156 FItems.insert(iterator, item); 157 break; 158 } 159 } 160 } 161 162 std::list<TString> JavaOptions::ToList() { 163 std::list<TString> result; 164 Macros& macros = Macros::GetInstance(); 165 166 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 167 iterator != FItems.end(); iterator++) { 168 TString key = iterator->name; 169 TString value = iterator->value; 170 TString option = Helpers::NameValueToString(key, value); 171 option = macros.ExpandMacros(option); 172 result.push_back(option); 173 } 174 175 return result; 176 } 177 178 size_t JavaOptions::GetCount() { 179 return FItems.size(); 180 } 181 182 // jvmuserargs can have a trailing equals in the key. This needs to be removed to use 183 // other parts of the launcher. 184 OrderedMap<TString, TString> RemoveTrailingEquals(OrderedMap<TString, TString> Map) { 185 OrderedMap<TString, TString> result; 186 187 std::vector<TString> keys = Map.GetKeys(); 188 189 for (size_t index = 0; index < keys.size(); index++) { 190 TString name = keys[index]; 191 TString value; 192 193 if (Map.GetValue(name, value) == true) { 194 // If the last character of the key is an equals, then remove it. If there is no 195 // equals then combine the two as a key. 196 TString::iterator i = name.end(); 197 i--; 198 199 if (*i == '=') { 200 name = name.substr(0, name.size() - 1); 201 } 202 else { 203 i = value.begin(); 204 205 if (*i == '=') { 206 value = value.substr(1, value.size() - 1); 207 } 208 else { 209 name = name + value; 210 value = _T(""); 211 } 212 } 213 214 result.Append(name, value); 215 } 216 } 217 218 return result; 219 } 220 221 //-------------------------------------------------------------------------------------------------- 222 223 JavaVirtualMachine::JavaVirtualMachine() { 224 } 225 226 JavaVirtualMachine::~JavaVirtualMachine(void) { 227 } 228 229 bool JavaVirtualMachine::StartJVM() { 230 Platform& platform = Platform::GetInstance(); 231 Package& package = Package::GetInstance(); 232 233 TString classpath = package.GetClassPath(); 234 TString modulepath = package.GetModulePath(); 235 JavaOptions options; 236 237 if (modulepath.empty() == false) { 238 options.AppendValue(_T("-Djava.module.path"), modulepath); 239 } 240 241 options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() + FilePath::PathSeparator() + package.GetPackageLauncherDirectory()); 242 options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 243 options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); 244 options.AppendValues(package.GetJVMArgs()); 245 options.AppendValues(RemoveTrailingEquals(package.GetJVMUserArgs())); 246 247 #ifdef DEBUG 248 if (package.Debugging() == dsJava) { 249 options.AppendValue(_T("-Xdebug"), _T("")); 250 options.AppendValue(_T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), _T("")); 251 platform.ShowMessage(_T("localhost:5005")); 252 } 253 #endif //DEBUG 254 255 TString maxHeapSizeOption; 256 TString minHeapSizeOption; 257 258 259 if (package.GetMemoryState() == PackageBootFields::msAuto) { 260 TPlatformNumber memorySize = package.GetMemorySize(); 261 TString memory = PlatformString((size_t)memorySize).toString() + _T("m"); 262 maxHeapSizeOption = TString(_T("-Xmx")) + memory; 263 options.AppendValue(maxHeapSizeOption, _T("")); 264 265 if (memorySize > 256) 266 minHeapSizeOption = _T("-Xms256m"); 267 else 268 minHeapSizeOption = _T("-Xms") + memory; 269 270 options.AppendValue(minHeapSizeOption, _T("")); 271 } 272 273 TString mainClassName = package.GetMainClassName(); 274 TString mainModule = package.GetMainModule(); 275 276 if (mainClassName.empty() == true && mainModule.empty() == true) { 277 Messages& messages = Messages::GetInstance(); 278 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 279 return false; 280 } 281 282 configureLibrary(); 283 284 // Initialize the arguments to JLI_Launch() 285 // 286 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. This 287 // new thread simply re-runs main(argc, argv). Therefore we do not want 288 // to add new args if we are still in the original main thread so we 289 // will treat them as command line args provided by the user ... 290 // Only propagate original set of args first time. 291 292 options.AppendValue(_T("-classpath")); 293 options.AppendValue(classpath); 294 295 std::list<TString> vmargs; 296 vmargs.push_back(package.GetCommandName()); 297 298 if (package.HasSplashScreen() == true) { 299 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 300 } 301 302 if (mainModule.empty() == true) { 303 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), _T("")); 304 } 305 else { 306 options.AppendValue(_T("-m")); 307 options.AppendValue(mainModule); 308 } 309 310 return launchVM(options, vmargs, false); 311 } 312 313 bool JavaVirtualMachine::NotifySingleInstance() { 314 Package& package = Package::GetInstance(); 315 316 std::list<TString> vmargs; 317 vmargs.push_back(package.GetCommandName()); 318 319 JavaOptions options; 320 options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() 321 + FilePath::PathSeparator() + package.GetPackageLauncherDirectory()); 322 options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 323 // launch SingleInstanceNewActivation.main() to pass arguments to another instance 324 options.AppendValue(_T("-m")); 325 options.AppendValue(_T("jdk.packager.services/jdk.packager.services.singleton.SingleInstanceNewActivation")); 326 327 configureLibrary(); 328 329 return launchVM(options, vmargs, true); 330 } 331 332 void JavaVirtualMachine::configureLibrary() { 333 Platform& platform = Platform::GetInstance(); 334 Package& package = Package::GetInstance(); 335 // TODO: Clean this up. Because of bug JDK-8131321 the opening of the PE file 336 // fails in WindowsPlatform.cpp on the check to 337 // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) 338 TString libName = package.GetJVMLibraryFileName(); 339 #ifdef _WIN64 340 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 341 javaLibrary.AddDependency(_T("msvcr100.dll")); 342 } 343 #else 344 javaLibrary.AddDependencies( 345 platform.FilterOutRuntimeDependenciesForPlatform(platform.GetLibraryImports(libName))); 346 #endif 347 javaLibrary.Load(libName); 348 } 349 350 bool JavaVirtualMachine::launchVM(JavaOptions& options, std::list<TString>& vmargs, bool addSiProcessId) { 351 Platform& platform = Platform::GetInstance(); 352 Package& package = Package::GetInstance(); 353 354 #ifdef MAC 355 // Mac adds a ProcessSerialNumber to args when launched from .app 356 // filter out the psn since they it's not expected in the app 357 if (platform.IsMainThread() == false) { 358 std::list<TString> loptions = options.ToList(); 359 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 360 } 361 #else 362 std::list<TString> loptions = options.ToList(); 363 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 364 #endif 365 366 if (addSiProcessId) { 367 // add single instance process ID as a first argument 368 TProcessID pid = platform.GetSingleInstanceProcessId(); 369 std::ostringstream s; 370 s << pid; 371 std::string procIdStr(s.str()); 372 vmargs.push_back(TString(procIdStr.begin(), procIdStr.end())); 373 } 374 375 std::list<TString> largs = package.GetArgs(); 376 vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); 377 378 size_t argc = vmargs.size(); 379 DynamicBuffer<char*> argv(argc + 1); 380 unsigned int index = 0; 381 for (std::list<TString>::const_iterator iterator = vmargs.begin(); 382 iterator != vmargs.end(); iterator++) { 383 TString item = *iterator; 384 std::string arg = PlatformString(item).toStdString(); 385 #ifdef DEBUG 386 printf("%i %s\n", index, arg.c_str()); 387 #endif //DEBUG 388 argv[index] = PlatformString::duplicate(arg.c_str()); 389 index++; 390 } 391 392 argv[argc] = NULL; 393 394 // On Mac we can only free the boot fields if the calling thread is not the main thread. 395 #ifdef MAC 396 if (platform.IsMainThread() == false) { 397 package.FreeBootFields(); 398 } 399 #else 400 package.FreeBootFields(); 401 #endif //MAC 402 403 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 404 return true; 405 } 406 407 for (index = 0; index < argc; index++) { 408 if (argv[index] != NULL) { 409 delete[] argv[index]; 410 } 411 } 412 413 return false; 414 }