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 "Platform.h"
  27 #include "PlatformString.h"
  28 #include "FilePath.h"
  29 #include "PropertyFile.h"
  30 #include "JavaVirtualMachine.h"
  31 #include "Package.h"
  32 #include "Macros.h"
  33 #include "Messages.h"
  34 
  35 #include <stdio.h>
  36 #include <signal.h>
  37 #include <stdlib.h>
  38 
  39 /*
  40 This is the app launcher program for application packaging on Windows, Mac,
  41     and Linux.
  42 
  43 Basic approach:
  44   - Launcher (jpackageapplauncher) is executable that loads
  45     applauncher.dll/libapplauncher.dylib/libapplauncher.so
  46     and calls start_launcher below.
  47   - Reads app/package.cfg or Info.plist or app/<appname>.cfg for application
  48     launch configuration (package.cfg is property file).
  49   - Load JVM with requested JVM settings (bundled client JVM if availble,
  50     server or installed JVM otherwise).
  51   - Wait for JVM to exit and then exit from Main
  52   - To debug application by passing command line argument.
  53   - Application folder is added to the library path (so LoadLibrary()) works.
  54 
  55 Limitations and future work:
  56   - Running Java code in primordial thread may cause problems
  57     (example: can not use custom stack size).
  58     Solution used by java launcher is to create a new thread to invoke JVM.
  59     See CR 6316197 for more information.
  60 */
  61 
  62 extern "C" {
  63 
  64     JNIEXPORT bool start_launcher(int argc, TCHAR* argv[]) {
  65         bool result = false;
  66         bool parentProcess = true;
  67 
  68         // Platform must be initialize first.
  69         Platform& platform = Platform::GetInstance();
  70 
  71         try {
  72             for (int index = 0; index < argc; index++) {
  73                 TString argument = argv[index];
  74 
  75                 if (argument == _T("-Xappcds:generatecache")) {
  76                     platform.SetAppCDSState(cdsGenCache);
  77                 }
  78                 else if (argument == _T("-Xappcds:off")) {
  79                     platform.SetAppCDSState(cdsDisabled);
  80                 }
  81                 else if (argument == _T("-Xapp:child")) {
  82                     parentProcess = false;
  83                 }
  84 #ifdef DEBUG
  85                 // There is a compiler bug on Mac when overloading
  86                 // ShowResponseMessage.
  87                 else if (argument == _T("-nativedebug")) {
  88                     if (platform.ShowResponseMessage(_T("Test"),
  89                         TString(_T("Would you like to debug?\n\nProcessID: "))
  90                         + PlatformString(platform.GetProcessID()).toString())
  91                          == mrOK) {
  92                         while (platform.IsNativeDebuggerPresent() == false) {
  93                         }
  94                     }
  95                 }
  96 #endif //DEBUG
  97             }
  98 
  99             // Package must be initialized after Platform is fully initialized.
 100             Package& package = Package::GetInstance();
 101             Macros::Initialize();
 102             package.SetCommandLineArguments(argc, argv);
 103             platform.SetCurrentDirectory(package.GetPackageAppDirectory());
 104 
 105             switch (platform.GetAppCDSState()) {
 106                 case cdsDisabled:
 107                 case cdsUninitialized:
 108                 case cdsEnabled: {
 109                     break;
 110                 }
 111 
 112                 case cdsGenCache: {
 113                     TString cacheDirectory = package.GetAppCDSCacheDirectory();
 114 
 115                     if (FilePath::DirectoryExists(cacheDirectory) == false) {
 116                         FilePath::CreateDirectory(cacheDirectory, true);
 117                     } else {
 118                         TString cacheFileName =
 119                                 package.GetAppCDSCacheFileName();
 120                         if (FilePath::FileExists(cacheFileName) == true) {
 121                             FilePath::DeleteFile(cacheFileName);
 122                         }
 123                     }
 124 
 125                     break;
 126                 }
 127 
 128                 case cdsAuto: {
 129                     TString cacheFileName = package.GetAppCDSCacheFileName();
 130 
 131                     if (parentProcess == true &&
 132                             FilePath::FileExists(cacheFileName) == false) {
 133                         AutoFreePtr<Process> process = platform.CreateProcess();
 134                         std::vector<TString> args;
 135                         args.push_back(_T("-Xappcds:generatecache"));
 136                         args.push_back(_T("-Xapp:child"));
 137                         process->Execute(
 138                                 platform.GetModuleFileName(), args, true);
 139 
 140                         if (FilePath::FileExists(cacheFileName) == false) {
 141                             // Cache does not exist after trying to generate it,
 142                             // so run without cache.
 143                             platform.SetAppCDSState(cdsDisabled);
 144                             package.Clear();
 145                             package.Initialize();
 146                         }
 147                     }
 148 
 149                     break;
 150                 }
 151             }
 152 
 153             // Validation
 154             switch (platform.GetAppCDSState()) {
 155                 case cdsDisabled:
 156                 case cdsGenCache: {
 157                     // Do nothing.
 158                     break;
 159                 }
 160 
 161                 case cdsEnabled:
 162                 case cdsAuto: {
 163                     TString cacheFileName =
 164                             package.GetAppCDSCacheFileName();
 165 
 166                     if (FilePath::FileExists(cacheFileName) == false) {
 167                         Messages& messages = Messages::GetInstance();
 168                         TString message = PlatformString::Format(
 169                                 messages.GetMessage(
 170                                 APPCDS_CACHE_FILE_NOT_FOUND),
 171                                 cacheFileName.data());
 172                         throw Exception(message);
 173                     }
 174                     break;
 175                 }
 176 
 177                 case cdsUninitialized: {
 178                     platform.ShowMessage(_T("Internal Error"));
 179                     break;
 180                 }
 181             }
 182 
 183             // Run App
 184             result = RunVM();
 185         } catch (Exception &e) {
 186             platform.ShowMessage(e.GetMessage());
 187         }
 188 
 189         return result;
 190     }
 191 
 192     JNIEXPORT void stop_launcher() {
 193     }
 194 }