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 }