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