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