1 /* 2 * Copyright (c) 2014, 2018, 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 #include "JavaVirtualMachine.h" 27 #include "Platform.h" 28 #include "PlatformString.h" 29 #include "FilePath.h" 30 #include "Package.h" 31 #include "JavaTypes.h" 32 #include "Helpers.h" 33 #include "Messages.h" 34 #include "Macros.h" 35 #include "PlatformThread.h" 36 37 #include "jni.h" 38 39 #include <map> 40 #include <list> 41 #include <sstream> 42 43 44 bool RunVM(JvmLaunchType type) { 45 bool result = false; 46 JavaVirtualMachine javavm; 47 48 switch (type){ 49 case USER_APP_LAUNCH: 50 result = javavm.StartJVM(); 51 break; 52 case SINGLE_INSTANCE_NOTIFICATION_LAUNCH: 53 result = javavm.NotifySingleInstance(); 54 break; 55 default: 56 break; 57 } 58 59 if (!result) { 60 Platform& platform = Platform::GetInstance(); 61 platform.ShowMessage(_T("Failed to launch JVM\n")); 62 } 63 64 return result; 65 } 66 67 JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL) { 68 } 69 70 bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) { 71 if (FCreateProc == NULL) { 72 FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC); 73 } 74 75 if (FCreateProc == NULL) { 76 Platform& platform = Platform::GetInstance(); 77 Messages& messages = Messages::GetInstance(); 78 platform.ShowMessage( 79 messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); 80 return false; 81 } 82 83 return FCreateProc((int)argc, argv, 84 0, NULL, 85 0, NULL, 86 "", 87 "", 88 "java", 89 "java", 90 false, 91 false, 92 false, 93 0) == 0; 94 } 95 96 //---------------------------------------------------------------------------- 97 98 JavaOptions::JavaOptions(): FOptions(NULL) { 99 } 100 101 JavaOptions::~JavaOptions() { 102 if (FOptions != NULL) { 103 for (unsigned int index = 0; index < GetCount(); index++) { 104 delete[] FOptions[index].optionString; 105 } 106 107 delete[] FOptions; 108 } 109 } 110 111 void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) { 112 JavaOptionItem item; 113 item.name = Key; 114 item.value = Value; 115 item.extraInfo = Extra; 116 FItems.push_back(item); 117 } 118 119 void JavaOptions::AppendValue(const TString Key, TString Value) { 120 AppendValue(Key, Value, NULL); 121 } 122 123 void JavaOptions::AppendValue(const TString Key) { 124 AppendValue(Key, _T(""), NULL); 125 } 126 127 void JavaOptions::AppendValues(OrderedMap<TString, TString> Values) { 128 std::vector<TString> orderedKeys = Values.GetKeys(); 129 130 for (std::vector<TString>::const_iterator iterator = orderedKeys.begin(); 131 iterator != orderedKeys.end(); iterator++) { 132 TString name = *iterator; 133 TString value; 134 135 if (Values.GetValue(name, value) == true) { 136 AppendValue(name, value); 137 } 138 } 139 } 140 141 void JavaOptions::ReplaceValue(const TString Key, TString Value) { 142 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 143 iterator != FItems.end(); iterator++) { 144 145 TString lkey = iterator->name; 146 147 if (lkey == Key) { 148 JavaOptionItem item = *iterator; 149 item.value = Value; 150 iterator = FItems.erase(iterator); 151 FItems.insert(iterator, item); 152 break; 153 } 154 } 155 } 156 157 std::list<TString> JavaOptions::ToList() { 158 std::list<TString> result; 159 Macros& macros = Macros::GetInstance(); 160 161 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 162 iterator != FItems.end(); iterator++) { 163 TString key = iterator->name; 164 TString value = iterator->value; 165 TString option = Helpers::NameValueToString(key, value); 166 option = macros.ExpandMacros(option); 167 result.push_back(option); 168 } 169 170 return result; 171 } 172 173 size_t JavaOptions::GetCount() { 174 return FItems.size(); 175 } 176 177 //---------------------------------------------------------------------------- 178 179 JavaVirtualMachine::JavaVirtualMachine() { 180 } 181 182 JavaVirtualMachine::~JavaVirtualMachine(void) { 183 } 184 185 bool JavaVirtualMachine::StartJVM() { 186 Platform& platform = Platform::GetInstance(); 187 Package& package = Package::GetInstance(); 188 189 TString classpath = package.GetClassPath(); 190 TString modulepath = package.GetModulePath(); 191 JavaOptions options; 192 193 if (modulepath.empty() == false) { 194 options.AppendValue(_T("-Djava.module.path"), modulepath); 195 } 196 197 options.AppendValue(_T("-Djava.library.path"), 198 package.GetPackageAppDirectory() + FilePath::PathSeparator() 199 + package.GetPackageLauncherDirectory()); 200 options.AppendValue( 201 _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 202 options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); 203 options.AppendValues(package.GetJVMArgs()); 204 205 #ifdef DEBUG 206 if (package.Debugging() == dsJava) { 207 options.AppendValue(_T("-Xdebug"), _T("")); 208 options.AppendValue( 209 _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), 210 _T("")); 211 platform.ShowMessage(_T("localhost:5005")); 212 } 213 #endif // DEBUG 214 215 TString maxHeapSizeOption; 216 TString minHeapSizeOption; 217 218 219 if (package.GetMemoryState() == PackageBootFields::msAuto) { 220 TPlatformNumber memorySize = package.GetMemorySize(); 221 TString memory = 222 PlatformString((size_t)memorySize).toString() + _T("m"); 223 maxHeapSizeOption = TString(_T("-Xmx")) + memory; 224 options.AppendValue(maxHeapSizeOption, _T("")); 225 226 if (memorySize > 256) 227 minHeapSizeOption = _T("-Xms256m"); 228 else 229 minHeapSizeOption = _T("-Xms") + memory; 230 231 options.AppendValue(minHeapSizeOption, _T("")); 232 } 233 234 TString mainClassName = package.GetMainClassName(); 235 TString mainModule = package.GetMainModule(); 236 237 if (mainClassName.empty() == true && mainModule.empty() == true) { 238 Messages& messages = Messages::GetInstance(); 239 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 240 return false; 241 } 242 243 configureLibrary(); 244 245 // Initialize the arguments to JLI_Launch() 246 // 247 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. 248 // This new thread simply re-runs main(argc, argv). Therefore we do not 249 // want to add new args if we are still in the original main thread so we 250 // will treat them as command line args provided by the user ... 251 // Only propagate original set of args first time. 252 253 options.AppendValue(_T("-classpath")); 254 options.AppendValue(classpath); 255 256 std::list<TString> vmargs; 257 vmargs.push_back(package.GetCommandName()); 258 259 if (package.HasSplashScreen() == true) { 260 options.AppendValue(TString(_T("-splash:")) 261 + package.GetSplashScreenFileName(), _T("")); 262 } 263 264 if (mainModule.empty() == true) { 265 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), 266 _T("")); 267 } else { 268 options.AppendValue(_T("-m")); 269 options.AppendValue(mainModule); 270 } 271 272 return launchVM(options, vmargs, false); 273 } 274 275 bool JavaVirtualMachine::NotifySingleInstance() { 276 Package& package = Package::GetInstance(); 277 278 std::list<TString> vmargs; 279 vmargs.push_back(package.GetCommandName()); 280 281 JavaOptions options; 282 options.AppendValue(_T("-Djava.library.path"), 283 package.GetPackageAppDirectory() + FilePath::PathSeparator() 284 + package.GetPackageLauncherDirectory()); 285 options.AppendValue(_T("-Djava.launcher.path"), 286 package.GetPackageLauncherDirectory()); 287 // launch SingleInstanceNewActivation.main() to pass arguments to 288 // another instance 289 options.AppendValue(_T("-m")); 290 options.AppendValue( 291 _T("jdk.jpackager.runtime/jdk.jpackager.runtime.singleton.SingleInstanceNewActivation")); 292 293 configureLibrary(); 294 295 return launchVM(options, vmargs, true); 296 } 297 298 void JavaVirtualMachine::configureLibrary() { 299 Platform& platform = Platform::GetInstance(); 300 Package& package = Package::GetInstance(); 301 // TODO: Clean this up. Because of bug JDK-8131321 the opening of the 302 // PE file ails in WindowsPlatform.cpp on the check to 303 // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) 304 TString libName = package.GetJVMLibraryFileName(); 305 #ifdef _WIN64 306 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 307 javaLibrary.AddDependency(_T("msvcr100.dll")); 308 } 309 310 TString runtimeBin = platform.GetPackageRuntimeBinDirectory(); 311 SetDllDirectory(runtimeBin.c_str()); 312 #else 313 javaLibrary.AddDependencies( 314 platform.FilterOutRuntimeDependenciesForPlatform( 315 platform.GetLibraryImports(libName))); 316 #endif 317 javaLibrary.Load(libName); 318 } 319 320 bool JavaVirtualMachine::launchVM(JavaOptions& options, 321 std::list<TString>& vmargs, bool addSiProcessId) { 322 Platform& platform = Platform::GetInstance(); 323 Package& package = Package::GetInstance(); 324 325 #ifdef MAC 326 // Mac adds a ProcessSerialNumber to args when launched from .app 327 // filter out the psn since they it's not expected in the app 328 if (platform.IsMainThread() == false) { 329 std::list<TString> loptions = options.ToList(); 330 vmargs.splice(vmargs.end(), loptions, 331 loptions.begin(), loptions.end()); 332 } 333 #else 334 std::list<TString> loptions = options.ToList(); 335 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 336 #endif 337 338 if (addSiProcessId) { 339 // add single instance process ID as a first argument 340 TProcessID pid = platform.GetSingleInstanceProcessId(); 341 std::ostringstream s; 342 s << pid; 343 std::string procIdStr(s.str()); 344 vmargs.push_back(TString(procIdStr.begin(), procIdStr.end())); 345 } 346 347 std::list<TString> largs = package.GetArgs(); 348 vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); 349 350 size_t argc = vmargs.size(); 351 DynamicBuffer<char*> argv(argc + 1); 352 if (argv.GetData() == NULL) { 353 return false; 354 } 355 356 unsigned int index = 0; 357 for (std::list<TString>::const_iterator iterator = vmargs.begin(); 358 iterator != vmargs.end(); iterator++) { 359 TString item = *iterator; 360 std::string arg = PlatformString(item).toStdString(); 361 #ifdef DEBUG 362 printf("%i %s\n", index, arg.c_str()); 363 #endif // DEBUG 364 argv[index] = PlatformString::duplicate(arg.c_str()); 365 index++; 366 } 367 368 argv[argc] = NULL; 369 370 // On Mac we can only free the boot fields if the calling thread is 371 // not the main thread. 372 #ifdef MAC 373 if (platform.IsMainThread() == false) { 374 package.FreeBootFields(); 375 } 376 #else 377 package.FreeBootFields(); 378 #endif // MAC 379 380 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 381 return true; 382 } 383 384 for (index = 0; index < argc; index++) { 385 if (argv[index] != NULL) { 386 delete[] argv[index]; 387 } 388 } 389 390 return false; 391 }