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