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