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 50 51 bool RunVM() { 52 bool result = false; 53 JavaVirtualMachine javavm; 54 55 if (javavm.StartJVM() == true) { 56 result = true; 57 javavm.ShutdownJVM(); 58 } 59 else { 60 Platform& platform = Platform::GetInstance(); 61 platform.ShowMessage(_T("Failed to launch JVM\n")); 62 } 63 64 return result; 65 } 66 67 68 // Private typedef for function pointer casting 69 #ifndef USE_JLI_LAUNCH 70 #define LAUNCH_FUNC "JNI_CreateJavaVM" 71 typedef jint (JNICALL *JVM_CREATE)(JavaVM ** jvm, JNIEnv ** env, void *); 72 #else 73 #define LAUNCH_FUNC "JLI_Launch" 74 typedef int (JNICALL *JVM_CREATE)(int argc, char ** argv, 75 int jargc, const char** jargv, 76 int appclassc, const char** appclassv, 77 const char* fullversion, 78 const char* dotversion, 79 const char* pname, 80 const char* lname, 81 jboolean javaargs, 82 jboolean cpwildcard, 83 jboolean javaw, 84 jint ergo); 85 #endif //USE_JLI_LAUNCH 86 87 class JavaLibrary : public Library { 88 JVM_CREATE FCreateProc; 89 90 JavaLibrary(const TString &FileName); 91 92 public: 93 JavaLibrary() : Library() { 94 FCreateProc = NULL; 95 } 96 97 #ifndef USE_JLI_LAUNCH 98 bool JavaVMCreate(JavaVM** jvm, JNIEnv** env, void* jvmArgs) { 99 bool result = true; 100 101 if (FCreateProc == NULL) { 102 FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC); 103 } 104 105 if (FCreateProc == NULL) { 106 Platform& platform = Platform::GetInstance(); 107 Messages& messages = Messages::GetInstance(); 108 platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); 109 return false; 110 } 111 112 if ((*FCreateProc)(jvm, env, jvmArgs) < 0) { 113 Platform& platform = Platform::GetInstance(); 114 Messages& messages = Messages::GetInstance(); 115 platform.ShowMessage(messages.GetMessage(FAILED_CREATING_JVM)); 116 return false; 117 } 118 119 return result; 120 } 121 #else 122 bool JavaVMCreate(size_t argc, char *argv[]) { 123 if (FCreateProc == NULL) { 124 FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC); 125 } 126 127 if (FCreateProc == NULL) { 128 Platform& platform = Platform::GetInstance(); 129 Messages& messages = Messages::GetInstance(); 130 platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); 131 return false; 132 } 133 134 return FCreateProc((int)argc, argv, 135 0, NULL, 136 0, NULL, 137 "", 138 "", 139 "java", 140 "java", 141 false, 142 false, 143 false, 144 0) == 0; 145 } 146 #endif //USE_JLI_LAUNCH 147 }; 148 149 #ifndef USE_JLI_LAUNCH 150 //debug hook to print JVM messages into console. 151 static jint JNICALL vfprintfHook(FILE *fp, const char *format, va_list args) { 152 #ifdef WINDOWS 153 char buffer[20480]; 154 int len; 155 HANDLE hConsole; 156 DWORD wasWritten; 157 158 len = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), format, args); 159 160 if (len <= 0) { 161 return len; 162 } 163 164 hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 165 166 if (hConsole == INVALID_HANDLE_VALUE) { 167 return false; 168 } 169 170 //JVM will always pass us ASCII 171 WriteConsoleA(hConsole, buffer, strlen(buffer), &wasWritten, NULL); 172 173 return (jint) len; 174 #endif //WINDOWS 175 #ifdef LINUX 176 return 0; 177 #endif //LINUX 178 } 179 #endif //USE_JLI_LAUNCH 180 181 //-------------------------------------------------------------------------------------------------- 182 183 struct JavaOptionItem { 184 TString name; 185 TString value; 186 void* extraInfo; 187 }; 188 189 190 class JavaOptions { 191 private: 192 std::list<JavaOptionItem> FItems; 193 JavaVMOption* FOptions; 194 195 public: 196 JavaOptions() { 197 FOptions = NULL; 198 199 #ifndef USE_JLI_LAUNCH 200 #ifdef DEBUG 201 Platform& platform = Platform::GetInstance(); 202 203 if (platform.GetDebugState() == dsNative) { 204 AppendValue(_T("vfprintf"), _T(""), (void*)vfprintfHook); 205 } 206 #endif //DEBUG 207 #endif //USE_JLI_LAUNCH 208 } 209 210 ~JavaOptions() { 211 if (FOptions != NULL) { 212 for (unsigned int index = 0; index < GetCount(); index++) { 213 delete[] FOptions[index].optionString; 214 } 215 216 delete[] FOptions; 217 } 218 } 219 220 void AppendValue(const TString Key, TString Value, void* Extra) { 221 JavaOptionItem item; 222 item.name = Key; 223 item.value = Value; 224 item.extraInfo = Extra; 225 FItems.push_back(item); 226 } 227 228 void AppendValue(const TString Key, TString Value) { 229 AppendValue(Key, Value, NULL); 230 } 231 232 void AppendValue(const TString Key) { 233 AppendValue(Key, _T(""), NULL); 234 } 235 236 void AppendValues(OrderedMap<TString, TString> Values) { 237 std::vector<TString> orderedKeys = Values.GetKeys(); 238 239 for (std::vector<TString>::const_iterator iterator = orderedKeys.begin(); iterator != orderedKeys.end(); iterator++) { 240 TString name = *iterator; 241 TString value; 242 243 if (Values.GetValue(name, value) == true) { 244 AppendValue(name, value); 245 } 246 } 247 } 248 249 void ReplaceValue(const TString Key, TString Value) { 250 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 251 iterator != FItems.end(); iterator++) { 252 253 TString lkey = iterator->name; 254 255 if (lkey == Key) { 256 JavaOptionItem item = *iterator; 257 item.value = Value; 258 iterator = FItems.erase(iterator); 259 FItems.insert(iterator, item); 260 break; 261 } 262 } 263 } 264 265 #ifndef USE_JLI_LAUNCH 266 JavaVMOption* ToJavaOptions() { 267 FOptions = new JavaVMOption[FItems.size()]; 268 memset(FOptions, 0, sizeof(JavaVMOption) * FItems.size()); 269 Macros& macros = Macros::GetInstance(); 270 unsigned int index = 0; 271 272 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 273 iterator != FItems.end(); iterator++) { 274 TString key = iterator->name; 275 TString value = iterator->value; 276 TString option = Helpers::NameValueToString(key, value); 277 option = macros.ExpandMacros(option); 278 #ifdef DEBUG 279 printf("%s\n", PlatformString(option).c_str()); 280 #endif //DEBUG 281 FOptions[index].optionString = PlatformString::duplicate(PlatformString(option).c_str()); 282 FOptions[index].extraInfo = iterator->extraInfo; 283 index++; 284 } 285 286 return FOptions; 287 } 288 #else 289 std::list<TString> ToList() { 290 std::list<TString> result; 291 Macros& macros = Macros::GetInstance(); 292 293 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 294 iterator != FItems.end(); iterator++) { 295 TString key = iterator->name; 296 TString value = iterator->value; 297 TString option = Helpers::NameValueToString(key, value); 298 option = macros.ExpandMacros(option); 299 result.push_back(option); 300 } 301 302 return result; 303 } 304 #endif //USE_JLI_LAUNCH 305 306 size_t GetCount() { 307 return FItems.size(); 308 } 309 }; 310 311 // jvmuserargs can have a trailing equals in the key. This needs to be removed to use 312 // other parts of the launcher. 313 OrderedMap<TString, TString> RemoveTrailingEquals(OrderedMap<TString, TString> Map) { 314 OrderedMap<TString, TString> result; 315 316 std::vector<TString> keys = Map.GetKeys(); 317 318 for (size_t index = 0; index < keys.size(); index++) { 319 TString name = keys[index]; 320 TString value; 321 322 if (Map.GetValue(name, value) == true) { 323 // If the last character of the key is an equals, then remove it. If there is no 324 // equals then combine the two as a key. 325 TString::iterator i = name.end(); 326 i--; 327 328 if (*i == '=') { 329 name = name.substr(0, name.size() - 1); 330 } 331 else { 332 i = value.begin(); 333 334 if (*i == '=') { 335 value = value.substr(1, value.size() - 1); 336 } 337 else { 338 name = name + value; 339 value = _T(""); 340 } 341 } 342 343 result.Append(name, value); 344 } 345 } 346 347 return result; 348 } 349 350 //-------------------------------------------------------------------------------------------------- 351 352 JavaVirtualMachine::JavaVirtualMachine() { 353 #ifndef USE_JLI_LAUNCH 354 FEnv = NULL; 355 FJvm = NULL; 356 #endif //USE_JLI_LAUNCH 357 } 358 359 JavaVirtualMachine::~JavaVirtualMachine(void) { 360 } 361 362 bool JavaVirtualMachine::StartJVM() { 363 Platform& platform = Platform::GetInstance(); 364 Package& package = Package::GetInstance(); 365 366 TString classpath = package.GetClassPath(); 367 TString modulepath = package.GetModulePath(); 368 JavaOptions options; 369 370 if (modulepath.empty() == false) { 371 options.AppendValue(_T("-Djava.module.path"), modulepath); 372 } 373 374 options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() + FilePath::PathSeparator() + package.GetPackageLauncherDirectory()); 375 options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 376 options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); 377 options.AppendValues(package.GetJVMArgs()); 378 options.AppendValues(RemoveTrailingEquals(package.GetJVMUserArgs())); 379 380 #ifdef DEBUG 381 if (package.Debugging() == dsJava) { 382 options.AppendValue(_T("-Xdebug"), _T("")); 383 options.AppendValue(_T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), _T("")); 384 platform.ShowMessage(_T("localhost:5005")); 385 } 386 #endif //DEBUG 387 388 TString maxHeapSizeOption; 389 TString minHeapSizeOption; 390 391 392 if (package.GetMemoryState() == PackageBootFields::msAuto) { 393 TPlatformNumber memorySize = package.GetMemorySize(); 394 TString memory = PlatformString((size_t)memorySize).toString() + _T("m"); 395 maxHeapSizeOption = TString(_T("-Xmx")) + memory; 396 options.AppendValue(maxHeapSizeOption, _T("")); 397 398 if (memorySize > 256) 399 minHeapSizeOption = _T("-Xms256m"); 400 else 401 minHeapSizeOption = _T("-Xms") + memory; 402 403 options.AppendValue(minHeapSizeOption, _T("")); 404 } 405 406 TString mainClassName = package.GetMainClassName(); 407 TString mainModule = package.GetMainModule(); 408 409 if (mainClassName.empty() == true && mainModule.empty() == true) { 410 Messages& messages = Messages::GetInstance(); 411 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 412 return false; 413 } 414 415 JavaLibrary javaLibrary; 416 417 // TODO: Clean this up. Because of bug JDK-8131321 the opening of the PE file fails in WindowsPlatform.cpp on the check to 418 // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) 419 #ifdef _WIN64 420 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 421 javaLibrary.AddDependency(_T("msvcr100.dll")); 422 } 423 #else 424 javaLibrary.AddDependencies(platform.FilterOutRuntimeDependenciesForPlatform(platform.GetLibraryImports(package.GetJVMLibraryFileName()))); 425 #endif 426 427 javaLibrary.Load(package.GetJVMLibraryFileName()); 428 429 #ifndef USE_JLI_LAUNCH 430 options.AppendValue(_T("-Djava.class.path"), classpath); 431 432 if (package.HasSplashScreen() == true) { 433 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 434 } 435 436 // Set up the VM init args 437 JavaVMInitArgs jvmArgs; 438 memset(&jvmArgs, 0, sizeof(JavaVMInitArgs)); 439 jvmArgs.version = JNI_VERSION_1_6; 440 jvmArgs.options = options.ToJavaOptions(); 441 jvmArgs.nOptions = (jint)options.GetCount(); 442 jvmArgs.ignoreUnrecognized = JNI_TRUE; 443 444 if (javaLibrary.JavaVMCreate(&FJvm, &FEnv, &jvmArgs) == true) { 445 try { 446 JavaClass mainClass(FEnv, Helpers::ConvertIdToJavaPath(mainClassName)); 447 JavaStaticMethod mainMethod = mainClass.GetStaticMethod(_T("main"), _T("([Ljava/lang/String;)V")); 448 std::list<TString> appargs = package.GetArgs(); 449 JavaStringArray largs(FEnv, appargs); 450 451 package.FreeBootFields(); 452 453 mainMethod.CallVoidMethod(1, largs.GetData()); 454 return true; 455 } 456 catch (JavaException& exception) { 457 platform.ShowMessage(exception.GetMessage()); 458 return false; 459 } 460 } 461 462 return false; 463 } 464 #else 465 // Initialize the arguments to JLI_Launch() 466 // 467 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. This 468 // new thread simply re-runs main(argc, argv). Therefore we do not want 469 // to add new args if we are still in the original main thread so we 470 // will treat them as command line args provided by the user ... 471 // Only propagate original set of args first time. 472 473 options.AppendValue(_T("-classpath")); 474 options.AppendValue(classpath); 475 476 std::list<TString> vmargs; 477 vmargs.push_back(package.GetCommandName()); 478 479 if (package.HasSplashScreen() == true) { 480 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 481 } 482 483 if (mainModule.empty() == true) { 484 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), _T("")); 485 } 486 else { 487 options.AppendValue(_T("-m")); 488 options.AppendValue(mainModule); 489 } 490 491 #ifdef MAC 492 // Mac adds a ProcessSerialNumber to args when launched from .app 493 // filter out the psn since they it's not expected in the app 494 if (platform.IsMainThread() == false) { 495 std::list<TString> loptions = options.ToList(); 496 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 497 } 498 #else 499 std::list<TString> loptions = options.ToList(); 500 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 501 #endif 502 503 std::list<TString> largs = package.GetArgs(); 504 vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); 505 size_t argc = vmargs.size(); 506 DynamicBuffer<char*> argv(argc + 1); 507 unsigned int index = 0; 508 509 for (std::list<TString>::const_iterator iterator = vmargs.begin(); 510 iterator != vmargs.end(); iterator++) { 511 TString item = *iterator; 512 std::string arg = PlatformString(item).toStdString(); 513 #ifdef DEBUG 514 printf("%i %s\n", index, arg.c_str()); 515 #endif //DEBUG 516 argv[index] = PlatformString::duplicate(arg.c_str()); 517 index++; 518 } 519 520 argv[argc] = NULL; 521 522 // On Mac we can only free the boot fields if the calling thread is not the main thread. 523 #ifdef MAC 524 if (platform.IsMainThread() == false) { 525 package.FreeBootFields(); 526 } 527 #else 528 package.FreeBootFields(); 529 #endif //MAC 530 531 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 532 return true; 533 } 534 535 for (index = 0; index < argc; index++) { 536 if (argv[index] != NULL) { 537 delete[] argv[index]; 538 } 539 } 540 541 return false; 542 } 543 #endif //USE_JLI_LAUNCH 544 545 void JavaVirtualMachine::ShutdownJVM() { 546 #ifndef USE_JLI_LAUNCH 547 if (FJvm != NULL) { 548 // If application main() exits quickly but application is run on some other thread 549 // (e.g. Swing app performs invokeLater() in main and exits) 550 // then if we return execution to tWinMain it will exit. 551 // This will cause process to exit and application will not actually run. 552 // 553 // To avoid this we are trying to detach jvm from current thread (java.exe does the same) 554 // Because we are doing this on the main JVM thread (i.e. one that was used to create JVM) 555 // this call will spawn "Destroy Java VM" java thread that will shut JVM once there are 556 // no non-daemon threads running, and then return control here. 557 // I.e. this will happen when EDT and other app thread will exit. 558 if (FJvm->DetachCurrentThread() != JNI_OK) { 559 Platform& platform = Platform::GetInstance(); 560 platform.ShowMessage(_T("Detach failed.")); 561 } 562 563 FJvm->DestroyJavaVM(); 564 } 565 #endif //USE_JLI_LAUNCH 566 }