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