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