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 }