1 /* 2 * Copyright (c) 2014, 2015, 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 AppendValues(OrderedMap<TString, TString> Values) { 233 std::vector<TString> orderedKeys = Values.GetKeys(); 234 235 for (std::vector<TString>::const_iterator iterator = orderedKeys.begin(); iterator != orderedKeys.end(); iterator++) { 236 TString name = *iterator; 237 TString value; 238 239 if (Values.GetValue(name, value) == true) { 240 AppendValue(name, value); 241 } 242 } 243 } 244 245 void ReplaceValue(const TString Key, TString Value) { 246 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 247 iterator != FItems.end(); iterator++) { 248 249 TString lkey = iterator->name; 250 251 if (lkey == Key) { 252 JavaOptionItem item = *iterator; 253 item.value = Value; 254 iterator = FItems.erase(iterator); 255 FItems.insert(iterator, item); 256 break; 257 } 258 } 259 } 260 261 #ifndef USE_JLI_LAUNCH 262 JavaVMOption* ToJavaOptions() { 263 FOptions = new JavaVMOption[FItems.size()]; 264 memset(FOptions, 0, sizeof(JavaVMOption) * FItems.size()); 265 Macros& macros = Macros::GetInstance(); 266 unsigned int index = 0; 267 268 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 269 iterator != FItems.end(); iterator++) { 270 TString key = iterator->name; 271 TString value = iterator->value; 272 TString option = Helpers::NameValueToString(key, value); 273 option = macros.ExpandMacros(option); 274 #ifdef DEBUG 275 printf("%s\n", PlatformString(option).c_str()); 276 #endif //DEBUG 277 FOptions[index].optionString = PlatformString::duplicate(PlatformString(option).c_str()); 278 FOptions[index].extraInfo = iterator->extraInfo; 279 index++; 280 } 281 282 return FOptions; 283 } 284 #else 285 std::list<TString> ToList() { 286 std::list<TString> result; 287 Macros& macros = Macros::GetInstance(); 288 289 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 290 iterator != FItems.end(); iterator++) { 291 TString key = iterator->name; 292 TString value = iterator->value; 293 TString option = Helpers::NameValueToString(key, value); 294 option = macros.ExpandMacros(option); 295 result.push_back(option); 296 } 297 298 return result; 299 } 300 #endif //USE_JLI_LAUNCH 301 302 size_t GetCount() { 303 return FItems.size(); 304 } 305 }; 306 307 // jvmuserargs can have a trailing equals in the key. This needs to be removed to use 308 // other parts of the launcher. 309 OrderedMap<TString, TString> RemoveTrailingEquals(OrderedMap<TString, TString> Map) { 310 OrderedMap<TString, TString> result; 311 312 std::vector<TString> keys = Map.GetKeys(); 313 314 for (size_t index = 0; index < keys.size(); index++) { 315 TString name = keys[index]; 316 TString value; 317 318 if (Map.GetValue(name, value) == true) { 319 // If the last character of the key is an equals, then remove it. If there is no 320 // equals then combine the two as a key. 321 TString::iterator i = name.end(); 322 i--; 323 324 if (*i == '=') { 325 name = name.substr(0, name.size() - 1); 326 } 327 else { 328 i = value.begin(); 329 330 if (*i == '=') { 331 value = value.substr(1, value.size() - 1); 332 } 333 else { 334 name = name + value; 335 value = _T(""); 336 } 337 } 338 339 result.Append(name, value); 340 } 341 } 342 343 return result; 344 } 345 346 //-------------------------------------------------------------------------------------------------- 347 348 JavaVirtualMachine::JavaVirtualMachine() { 349 #ifndef USE_JLI_LAUNCH 350 FEnv = NULL; 351 FJvm = NULL; 352 #endif //USE_JLI_LAUNCH 353 } 354 355 JavaVirtualMachine::~JavaVirtualMachine(void) { 356 } 357 358 bool JavaVirtualMachine::StartJVM() { 359 Platform& platform = Platform::GetInstance(); 360 Package& package = Package::GetInstance(); 361 362 TString classpath = package.GetClassPath(); 363 364 JavaOptions options; 365 options.AppendValue(_T("-Djava.class.path"), classpath); 366 options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() + FilePath::PathSeparator() + package.GetPackageLauncherDirectory()); 367 options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 368 options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); 369 options.AppendValues(package.GetJVMArgs()); 370 options.AppendValues(RemoveTrailingEquals(package.GetJVMUserArgs())); 371 372 #ifdef DEBUG 373 if (package.Debugging() == dsJava) { 374 options.AppendValue(_T("-Xdebug"), _T("")); 375 options.AppendValue(_T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:8000"), _T("")); 376 platform.ShowMessage(_T("localhost:8000")); 377 } 378 #endif //DEBUG 379 380 TString maxHeapSizeOption; 381 TString minHeapSizeOption; 382 383 384 if (package.GetMemoryState() == PackageBootFields::msAuto) { 385 TPlatformNumber memorySize = package.GetMemorySize(); 386 TString memory = PlatformString((size_t)memorySize).toString() + _T("m"); 387 maxHeapSizeOption = TString(_T("-Xmx")) + memory; 388 options.AppendValue(maxHeapSizeOption, _T("")); 389 390 if (memorySize > 256) 391 minHeapSizeOption = _T("-Xms256m"); 392 else 393 minHeapSizeOption = _T("-Xms") + memory; 394 395 options.AppendValue(minHeapSizeOption, _T("")); 396 } 397 398 TString mainClassName = package.GetMainClassName(); 399 400 if (mainClassName.empty() == true) { 401 Messages& messages = Messages::GetInstance(); 402 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 403 return false; 404 } 405 406 JavaLibrary javaLibrary; 407 408 // TODO: Clean this up. Because of bug JDK-8131321 the opening of the PE file fails in WindowsPlatform.cpp on the check to 409 // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) 410 #ifdef _WIN64 411 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 412 javaLibrary.AddDependency(_T("msvcr100.dll")); 413 } 414 #else 415 javaLibrary.AddDependencies(platform.FilterOutRuntimeDependenciesForPlatform(platform.GetLibraryImports(package.GetJVMLibraryFileName()))); 416 #endif 417 418 javaLibrary.Load(package.GetJVMLibraryFileName()); 419 420 #ifndef USE_JLI_LAUNCH 421 if (package.HasSplashScreen() == true) { 422 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 423 } 424 425 // Set up the VM init args 426 JavaVMInitArgs jvmArgs; 427 memset(&jvmArgs, 0, sizeof(JavaVMInitArgs)); 428 jvmArgs.version = JNI_VERSION_1_6; 429 jvmArgs.options = options.ToJavaOptions(); 430 jvmArgs.nOptions = (jint)options.GetCount(); 431 jvmArgs.ignoreUnrecognized = JNI_TRUE; 432 433 if (javaLibrary.JavaVMCreate(&FJvm, &FEnv, &jvmArgs) == true) { 434 try { 435 JavaClass mainClass(FEnv, Helpers::ConvertIdToJavaPath(mainClassName)); 436 JavaStaticMethod mainMethod = mainClass.GetStaticMethod(_T("main"), _T("([Ljava/lang/String;)V")); 437 std::list<TString> appargs = package.GetArgs(); 438 JavaStringArray largs(FEnv, appargs); 439 440 package.FreeBootFields(); 441 442 mainMethod.CallVoidMethod(1, largs.GetData()); 443 return true; 444 } 445 catch (JavaException& exception) { 446 platform.ShowMessage(exception.GetMessage()); 447 return false; 448 } 449 } 450 451 return false; 452 } 453 #else 454 // Initialize the arguments to JLI_Launch() 455 // 456 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. This 457 // new thread simply re-runs main(argc, argv). Therefore we do not want 458 // to add new args if we are still in the original main thread so we 459 // will treat them as command line args provided by the user ... 460 // Only propagate original set of args first time. 461 462 std::list<TString> vmargs; 463 vmargs.push_back(package.GetCommandName()); 464 465 if (package.HasSplashScreen() == true) { 466 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 467 } 468 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), _T("")); 469 470 #ifdef MAC 471 // Mac adds a ProcessSerialNumber to args when launched from .app 472 // filter out the psn since they it's not expected in the app 473 if (platform.IsMainThread() == false) { 474 std::list<TString> loptions = options.ToList(); 475 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 476 } 477 #else 478 std::list<TString> loptions = options.ToList(); 479 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 480 #endif 481 482 std::list<TString> largs = package.GetArgs(); 483 vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); 484 size_t argc = vmargs.size(); 485 DynamicBuffer<char*> argv(argc + 1); 486 unsigned int index = 0; 487 488 for (std::list<TString>::const_iterator iterator = vmargs.begin(); 489 iterator != vmargs.end(); iterator++) { 490 TString item = *iterator; 491 std::string arg = PlatformString(item).toStdString(); 492 #ifdef DEBUG 493 printf("%i %s\n", index, arg.c_str()); 494 #endif //DEBUG 495 argv[index] = PlatformString::duplicate(arg.c_str()); 496 index++; 497 } 498 499 argv[argc] = NULL; 500 501 // On Mac we can only free the boot fields if the calling thread is not the main thread. 502 #ifdef MAC 503 if (platform.IsMainThread() == false) { 504 package.FreeBootFields(); 505 } 506 #else 507 package.FreeBootFields(); 508 #endif //MAC 509 510 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 511 return true; 512 } 513 514 for (index = 0; index < argc; index++) { 515 if (argv[index] != NULL) { 516 delete[] argv[index]; 517 } 518 } 519 520 return false; 521 } 522 #endif //USE_JLI_LAUNCH 523 524 void JavaVirtualMachine::ShutdownJVM() { 525 #ifndef USE_JLI_LAUNCH 526 if (FJvm != NULL) { 527 // If application main() exits quickly but application is run on some other thread 528 // (e.g. Swing app performs invokeLater() in main and exits) 529 // then if we return execution to tWinMain it will exit. 530 // This will cause process to exit and application will not actually run. 531 // 532 // To avoid this we are trying to detach jvm from current thread (java.exe does the same) 533 // Because we are doing this on the main JVM thread (i.e. one that was used to create JVM) 534 // this call will spawn "Destroy Java VM" java thread that will shut JVM once there are 535 // no non-daemon threads running, and then return control here. 536 // I.e. this will happen when EDT and other app thread will exit. 537 if (FJvm->DetachCurrentThread() != JNI_OK) { 538 Platform& platform = Platform::GetInstance(); 539 platform.ShowMessage(_T("Detach failed.")); 540 } 541 542 FJvm->DestroyJavaVM(); 543 } 544 #endif //USE_JLI_LAUNCH 545 }