1 /*
   2  * Copyright (c) 2014, 2019, 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 "Package.h"
  27 #include "Helpers.h"
  28 #include "Macros.h"
  29 #include "IniFile.h"
  30 
  31 #include <assert.h>
  32 
  33 
  34 Package::Package(void) {
  35     FInitialized = false;
  36     Initialize();
  37 }
  38 
  39 TPlatformNumber StringToPercentageOfNumber(TString Value,
  40         TPlatformNumber Number) {
  41     TPlatformNumber result = 0;
  42     size_t percentage = atoi(PlatformString(Value.c_str()));
  43 
  44     if (percentage > 0 && Number > 0) {
  45         result = Number * percentage / 100;
  46     }
  47 
  48     return result;
  49 }
  50 
  51 void Package::Initialize() {
  52     if (FInitialized == true) {
  53         return;
  54     }
  55 
  56     Platform& platform = Platform::GetInstance();
  57 
  58     FBootFields = new PackageBootFields();
  59     FDebugging = dsNone;
  60 
  61     FBootFields->FPackageRootDirectory = platform.GetPackageRootDirectory();
  62     FBootFields->FPackageAppDirectory = platform.GetPackageAppDirectory();
  63     FBootFields->FPackageLauncherDirectory =
  64             platform.GetPackageLauncherDirectory();
  65     FBootFields->FAppDataDirectory = platform.GetAppDataDirectory();
  66 
  67     std::map<TString, TString> keys = platform.GetKeys();
  68 
  69     // Read from configure.cfg/Info.plist
  70     AutoFreePtr<ISectionalPropertyContainer> config =
  71             platform.GetConfigFile(platform.GetConfigFileName());
  72 
  73     config->GetValue(keys[CONFIG_SECTION_APPLICATION],
  74             keys[JPACKAGE_APP_DATA_DIR], FBootFields->FPackageAppDataDirectory);
  75     FBootFields->FPackageAppDataDirectory =
  76             FilePath::FixPathForPlatform(FBootFields->FPackageAppDataDirectory);
  77 
  78     // Main JAR.
  79     config->GetValue(keys[CONFIG_SECTION_APPLICATION],
  80             keys[CONFIG_MAINJAR_KEY], FBootFields->FMainJar);
  81     FBootFields->FMainJar = FilePath::FixPathForPlatform(FBootFields->FMainJar);
  82 
  83     // Main Module.
  84     config->GetValue(keys[CONFIG_SECTION_APPLICATION],
  85             keys[CONFIG_MAINMODULE_KEY], FBootFields->FMainModule);
  86 
  87     // Classpath.
  88     // 1. If the provided class path contains main jar then only use
  89     //    provided class path.
  90     // 2. If class path provided by config file is empty then add main jar.
  91     // 3. If main jar is not in provided class path then add it.
  92     config->GetValue(keys[CONFIG_SECTION_APPLICATION],
  93             keys[CONFIG_CLASSPATH_KEY], FBootFields->FClassPath);
  94     FBootFields->FClassPath =
  95             FilePath::FixPathSeparatorForPlatform(FBootFields->FClassPath);
  96 
  97     if (FBootFields->FClassPath.empty() == true) {
  98         FBootFields->FClassPath = GetMainJar();
  99     } else if (FBootFields->FClassPath.find(GetMainJar()) == TString::npos) {
 100         FBootFields->FClassPath = GetMainJar()
 101                 + FilePath::PathSeparator() + FBootFields->FClassPath;
 102     }
 103 
 104     // Modulepath.
 105     config->GetValue(keys[CONFIG_SECTION_APPLICATION],
 106             keys[CONFIG_MODULEPATH_KEY], FBootFields->FModulePath);
 107     FBootFields->FModulePath =
 108             FilePath::FixPathSeparatorForPlatform(FBootFields->FModulePath);
 109 
 110     // Main Class.
 111     config->GetValue(keys[CONFIG_SECTION_APPLICATION],
 112             keys[CONFIG_MAINCLASSNAME_KEY], FBootFields->FMainClassName);
 113 
 114     // Splash Screen.
 115     if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
 116             keys[CONFIG_SPLASH_KEY],
 117             FBootFields->FSplashScreenFileName) == true) {
 118         FBootFields->FSplashScreenFileName =
 119             FilePath::IncludeTrailingSeparator(GetPackageAppDirectory())
 120             + FilePath::FixPathForPlatform(FBootFields->FSplashScreenFileName);
 121 
 122         if (FilePath::FileExists(FBootFields->FSplashScreenFileName) == false) {
 123             FBootFields->FSplashScreenFileName = _T("");
 124         }
 125     }
 126 
 127     // Runtime.
 128     config->GetValue(keys[CONFIG_SECTION_APPLICATION],
 129             keys[JAVA_RUNTIME_KEY], FBootFields->FJavaRuntimeDirectory);
 130 
 131     // Read jvmargs.
 132     PromoteAppCDSState(config);
 133     ReadJavaOptions(config);
 134 
 135     // Read args if none were passed in.
 136     if (FBootFields->FArgs.size() == 0) {
 137         OrderedMap<TString, TString> args;
 138 
 139         if (config->GetSection(keys[CONFIG_SECTION_ARGOPTIONS], args) == true) {
 140             FBootFields->FArgs = Helpers::MapToNameValueList(args);
 141         }
 142     }
 143 
 144     // Auto Memory.
 145     TString autoMemory;
 146 
 147     if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
 148             keys[CONFIG_APP_MEMORY], autoMemory) == true) {
 149         if (autoMemory == _T("auto") || autoMemory == _T("100%")) {
 150             FBootFields->FMemoryState = PackageBootFields::msAuto;
 151             FBootFields->FMemorySize = platform.GetMemorySize();
 152         } else if (autoMemory.length() == 2 && isdigit(autoMemory[0]) &&
 153                 autoMemory[1] == '%') {
 154             FBootFields->FMemoryState = PackageBootFields::msAuto;
 155             FBootFields->FMemorySize =
 156                     StringToPercentageOfNumber(autoMemory.substr(0, 1),
 157                     platform.GetMemorySize());
 158         } else if (autoMemory.length() == 3 && isdigit(autoMemory[0]) &&
 159                 isdigit(autoMemory[1]) && autoMemory[2] == '%') {
 160             FBootFields->FMemoryState = PackageBootFields::msAuto;
 161             FBootFields->FMemorySize =
 162                     StringToPercentageOfNumber(autoMemory.substr(0, 2),
 163                     platform.GetMemorySize());
 164         } else {
 165             FBootFields->FMemoryState = PackageBootFields::msManual;
 166             FBootFields->FMemorySize = 0;
 167         }
 168     }
 169 
 170     // Debug
 171     TString debug;
 172     if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
 173             keys[CONFIG_APP_DEBUG], debug) == true) {
 174         FBootFields->FArgs.push_back(debug);
 175     }
 176 }
 177 
 178 void Package::Clear() {
 179     FreeBootFields();
 180     FInitialized = false;
 181 }
 182 
 183 // This is the only location that the AppCDS state should be modified except
 184 // by command line arguments provided by the user.
 185 //
 186 // The state of AppCDS is as follows:
 187 //
 188 // -> cdsUninitialized
 189 //    -> cdsGenCache If -Xappcds:generatecache
 190 //    -> cdsDisabled If -Xappcds:off
 191 //    -> cdsEnabled If "AppCDSJavaOptions" section is present
 192 //    -> cdsAuto If "AppCDSJavaOptions" section is present and
 193 //               app.appcds.cache=auto
 194 //    -> cdsDisabled Default
 195 //
 196 void Package::PromoteAppCDSState(ISectionalPropertyContainer* Config) {
 197     Platform& platform = Platform::GetInstance();
 198     std::map<TString, TString> keys = platform.GetKeys();
 199 
 200     // The AppCDS state can change at this point.
 201     switch (platform.GetAppCDSState()) {
 202         case cdsEnabled:
 203         case cdsAuto:
 204         case cdsDisabled:
 205         case cdsGenCache: {
 206             // Do nothing.
 207             break;
 208         }
 209 
 210         case cdsUninitialized: {
 211             if (Config->ContainsSection(
 212                     keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]) == true) {
 213                 // If the AppCDS section is present then enable AppCDS.
 214                 TString appCDSCacheValue;
 215 
 216                 // If running with AppCDS enabled, and the configuration has
 217                 // been setup so "auto" is enabled, then
 218                 // the launcher will attempt to generate the cache file
 219                 // automatically and run the application.
 220                 if (Config->GetValue(keys[CONFIG_SECTION_APPLICATION],
 221                         _T("app.appcds.cache"), appCDSCacheValue) == true &&
 222                     appCDSCacheValue == _T("auto")) {
 223                     platform.SetAppCDSState(cdsAuto);
 224                 }
 225                 else {
 226                     platform.SetAppCDSState(cdsEnabled);
 227                 }
 228             } else {
 229 
 230                 platform.SetAppCDSState(cdsDisabled);
 231             }
 232         }
 233     }
 234 }
 235 
 236 void Package::ReadJavaOptions(ISectionalPropertyContainer* Config) {
 237     Platform& platform = Platform::GetInstance();
 238     std::map<TString, TString> keys = platform.GetKeys();
 239 
 240     // Evaluate based on the current AppCDS state.
 241     switch (platform.GetAppCDSState()) {
 242         case cdsUninitialized: {
 243             throw Exception(_T("Internal Error"));
 244         }
 245 
 246         case cdsDisabled: {
 247             Config->GetSection(keys[CONFIG_SECTION_JAVAOPTIONS],
 248                     FBootFields->FJavaOptions);
 249             break;
 250         }
 251 
 252         case cdsGenCache: {
 253             Config->GetSection(keys[
 254                     CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS],
 255                     FBootFields->FJavaOptions);
 256             break;
 257         }
 258 
 259         case cdsAuto:
 260         case cdsEnabled: {
 261             if (Config->GetValue(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS],
 262                     _T( "-XX:SharedArchiveFile"),
 263                     FBootFields->FAppCDSCacheFileName) == true) {
 264                 // File names may contain the incorrect path separators.
 265                 // The cache file name must be corrected at this point.
 266                 if (FBootFields->FAppCDSCacheFileName.empty() == false) {
 267                     IniFile* iniConfig = dynamic_cast<IniFile*>(Config);
 268 
 269                     if (iniConfig != NULL) {
 270                         FBootFields->FAppCDSCacheFileName =
 271                                 FilePath::FixPathForPlatform(
 272                                 FBootFields->FAppCDSCacheFileName);
 273                         iniConfig->SetValue(keys[
 274                                 CONFIG_SECTION_APPCDSJAVAOPTIONS],
 275                                 _T( "-XX:SharedArchiveFile"),
 276                                 FBootFields->FAppCDSCacheFileName);
 277                     }
 278                 }
 279 
 280                 Config->GetSection(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS],
 281                         FBootFields->FJavaOptions);
 282             }
 283 
 284             break;
 285         }
 286     }
 287 }
 288 
 289 void Package::SetCommandLineArguments(int argc, TCHAR* argv[]) {
 290     if (argc > 0) {
 291         std::list<TString> args;
 292 
 293         // Prepare app arguments. Skip value at index 0 -
 294         // this is path to executable.
 295         FBootFields->FCommandName = argv[0];
 296 
 297         // Path to executable is at 0 index so start at index 1.
 298         for (int index = 1; index < argc; index++) {
 299             TString arg = argv[index];
 300 
 301 #ifdef DEBUG
 302             if (arg == _T("-debug")) {
 303                 FDebugging = dsNative;
 304             }
 305 
 306             if (arg == _T("-javadebug")) {
 307                 FDebugging = dsJava;
 308             }
 309 #endif //DEBUG
 310 #ifdef MAC
 311             if (arg.find(_T("-psn_"), 0) != TString::npos) {
 312                 Platform& platform = Platform::GetInstance();
 313 
 314                 if (platform.IsMainThread() == true) {
 315 #ifdef DEBUG
 316                     printf("%s\n", arg.c_str());
 317 #endif //DEBUG
 318                     continue;
 319                 }
 320             }
 321 
 322             if (arg == _T("-NSDocumentRevisionsDebugMode")) {
 323                 // Ignore -NSDocumentRevisionsDebugMode and
 324                 // the following YES/NO
 325                 index++;
 326                 continue;
 327             }
 328 #endif //MAC
 329 
 330             args.push_back(arg);
 331         }
 332 
 333         if (args.size() > 0) {
 334             FBootFields->FArgs = args;
 335         }
 336     }
 337 }
 338 
 339 Package& Package::GetInstance() {
 340     static Package instance;
 341     // Guaranteed to be destroyed. Instantiated on first use.
 342     return instance;
 343 }
 344 
 345 Package::~Package(void) {
 346     FreeBootFields();
 347 }
 348 
 349 void Package::FreeBootFields() {
 350     if (FBootFields != NULL) {
 351         delete FBootFields;
 352         FBootFields = NULL;
 353     }
 354 }
 355 
 356 OrderedMap<TString, TString> Package::GetJavaOptions() {
 357     return FBootFields->FJavaOptions;
 358 }
 359 
 360 std::vector<TString> GetKeysThatAreNotDuplicates(OrderedMap<TString,
 361         TString> &Defaults, OrderedMap<TString, TString> &Overrides) {
 362     std::vector<TString> result;
 363     std::vector<TString> overrideKeys = Overrides.GetKeys();
 364 
 365     for (size_t index = 0; index < overrideKeys.size(); index++) {
 366         TString overridesKey = overrideKeys[index];
 367         TString overridesValue;
 368         TString defaultValue;
 369 
 370         if ((Defaults.ContainsKey(overridesKey) == false) ||
 371            (Defaults.GetValue(overridesKey, defaultValue) == true &&
 372             Overrides.GetValue(overridesKey, overridesValue) == true &&
 373             defaultValue != overridesValue)) {
 374             result.push_back(overridesKey);
 375         }
 376     }
 377 
 378     return result;
 379 }
 380 
 381 OrderedMap<TString, TString> CreateOrderedMapFromKeyList(OrderedMap<TString,
 382         TString> &Map, std::vector<TString> &Keys) {
 383     OrderedMap<TString, TString> result;
 384 
 385     for (size_t index = 0; index < Keys.size(); index++) {
 386         TString key = Keys[index];
 387         TString value;
 388 
 389         if (Map.GetValue(key, value) == true) {
 390             result.Append(key, value);
 391         }
 392     }
 393 
 394     return result;
 395 }
 396 
 397 std::vector<TString> GetKeysThatAreNotOverridesOfDefaultValues(
 398         OrderedMap<TString, TString> &Defaults, OrderedMap<TString,
 399         TString> &Overrides) {
 400     std::vector<TString> result;
 401     std::vector<TString> keys = Overrides.GetKeys();
 402 
 403     for (unsigned int index = 0; index< keys.size(); index++) {
 404         TString key = keys[index];
 405 
 406         if (Defaults.ContainsKey(key) == true) {
 407             try {
 408                 TString value = Overrides[key];
 409                 Defaults[key] = value;
 410             }
 411             catch (std::out_of_range &) {
 412             }
 413         }
 414         else {
 415             result.push_back(key);
 416         }
 417     }
 418 
 419     return result;
 420 }
 421 
 422 std::list<TString> Package::GetArgs() {
 423     assert(FBootFields != NULL);
 424     return FBootFields->FArgs;
 425 }
 426 
 427 TString Package::GetPackageRootDirectory() {
 428     assert(FBootFields != NULL);
 429     return FBootFields->FPackageRootDirectory;
 430 }
 431 
 432 TString Package::GetPackageAppDirectory() {
 433     assert(FBootFields != NULL);
 434     return FBootFields->FPackageAppDirectory;
 435 }
 436 
 437 TString Package::GetPackageLauncherDirectory() {
 438     assert(FBootFields != NULL);
 439     return FBootFields->FPackageLauncherDirectory;
 440 }
 441 
 442 TString Package::GetAppDataDirectory() {
 443     assert(FBootFields != NULL);
 444     return FBootFields->FAppDataDirectory;
 445 }
 446 
 447 TString Package::GetAppCDSCacheDirectory() {
 448     if (FAppCDSCacheDirectory.empty()) {
 449         Platform& platform = Platform::GetInstance();
 450         FAppCDSCacheDirectory = FilePath::IncludeTrailingSeparator(
 451                 platform.GetAppDataDirectory())
 452                 + FilePath::IncludeTrailingSeparator(
 453                 GetPackageAppDataDirectory()) + _T("cache");
 454 
 455         Macros& macros = Macros::GetInstance();
 456         FAppCDSCacheDirectory = macros.ExpandMacros(FAppCDSCacheDirectory);
 457         FAppCDSCacheDirectory =
 458                 FilePath::FixPathForPlatform(FAppCDSCacheDirectory);
 459     }
 460 
 461     return FAppCDSCacheDirectory;
 462 }
 463 
 464 TString Package::GetAppCDSCacheFileName() {
 465     assert(FBootFields != NULL);
 466 
 467     if (FBootFields->FAppCDSCacheFileName.empty() == false) {
 468         Macros& macros = Macros::GetInstance();
 469         FBootFields->FAppCDSCacheFileName =
 470                 macros.ExpandMacros(FBootFields->FAppCDSCacheFileName);
 471         FBootFields->FAppCDSCacheFileName =
 472                 FilePath::FixPathForPlatform(FBootFields->FAppCDSCacheFileName);
 473     }
 474 
 475     return FBootFields->FAppCDSCacheFileName;
 476 }
 477 
 478 TString Package::GetPackageAppDataDirectory() {
 479     assert(FBootFields != NULL);
 480     return FBootFields->FPackageAppDataDirectory;
 481 }
 482 
 483 TString Package::GetClassPath() {
 484     assert(FBootFields != NULL);
 485     return FBootFields->FClassPath;
 486 }
 487 
 488 TString Package::GetModulePath() {
 489     assert(FBootFields != NULL);
 490     return FBootFields->FModulePath;
 491 }
 492 
 493 TString Package::GetMainJar() {
 494     assert(FBootFields != NULL);
 495     return FBootFields->FMainJar;
 496 }
 497 
 498 TString Package::GetMainModule() {
 499     assert(FBootFields != NULL);
 500     return FBootFields->FMainModule;
 501 }
 502 
 503 TString Package::GetMainClassName() {
 504     assert(FBootFields != NULL);
 505     return FBootFields->FMainClassName;
 506 }
 507 
 508 TString Package::GetJavaLibraryFileName() {
 509     assert(FBootFields != NULL);
 510 
 511     if (FBootFields->FJavaLibraryFileName.empty() == true) {
 512         Platform& platform = Platform::GetInstance();
 513         Macros& macros = Macros::GetInstance();
 514         TString jvmRuntimePath = macros.ExpandMacros(GetJavaRuntimeDirectory());
 515         FBootFields->FJavaLibraryFileName =
 516                 platform.GetBundledJavaLibraryFileName(jvmRuntimePath);
 517     }
 518 
 519     return FBootFields->FJavaLibraryFileName;
 520 }
 521 
 522 TString Package::GetJavaRuntimeDirectory() {
 523     assert(FBootFields != NULL);
 524     return FBootFields->FJavaRuntimeDirectory;
 525 }
 526 
 527 TString Package::GetSplashScreenFileName() {
 528     assert(FBootFields != NULL);
 529     return FBootFields->FSplashScreenFileName;
 530 }
 531 
 532 bool Package::HasSplashScreen() {
 533     assert(FBootFields != NULL);
 534     return FilePath::FileExists(FBootFields->FSplashScreenFileName);
 535 }
 536 
 537 TString Package::GetCommandName() {
 538     assert(FBootFields != NULL);
 539     return FBootFields->FCommandName;
 540 }
 541 
 542 TPlatformNumber Package::GetMemorySize() {
 543     assert(FBootFields != NULL);
 544     return FBootFields->FMemorySize;
 545 }
 546 
 547 PackageBootFields::MemoryState Package::GetMemoryState() {
 548     assert(FBootFields != NULL);
 549     return FBootFields->FMemoryState;
 550 }
 551 
 552 DebugState Package::Debugging() {
 553     return FDebugging;
 554 }