1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 
  33 
  34 #include "Platform.h"
  35 #include "PlatformString.h"
  36 #include "FilePath.h"
  37 #include "PropertyFile.h"
  38 #include "JavaVirtualMachine.h"
  39 #include "Package.h"
  40 #include "PlatformThread.h"
  41 #include "Macros.h"
  42 #include "Messages.h"
  43 
  44 
  45 #ifdef WINDOWS
  46 #include <Shellapi.h>
  47 #endif
  48 
  49 
  50 #include <stdio.h>
  51 #include <signal.h>
  52 #include <stdlib.h>
  53 
  54 /*
  55 This is the launcher program for application packaging on Windows, Mac and Linux.
  56 
  57 Basic approach:
  58   - Launcher executable loads packager.dll/libpackager.dylib/libpackager.so and calls start_launcher below.
  59   - Reads app/package.cfg or Info.plist or app/<appname>.cfg for application launch configuration
  60      (package.cfg is property file).
  61   - Load JVM with requested JVM settings (bundled client JVM if availble, server or installed JVM otherwise).
  62   - Wait for JVM to exit and then exit from Main
  63   - To debug application by set env variable (TODO) or pass "/Debug" option on command line.
  64   - TODO: default directory is set to user's Documents and Settings.
  65   - Application folder is added to the library path (so LoadLibrary()) works.
  66 
  67 Limitations and future work:
  68   - Running Java code in primordial thread may cause problems
  69     (example: can not use custom stack size).
  70     Solution used by java launcher is to create a new thread to invoke JVM.
  71     See CR 6316197 for more information.
  72 */
  73 
  74 extern "C" {
  75 
  76 #ifdef WINDOWS
  77     BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
  78         return true;
  79     }
  80 
  81     __declspec(dllexport)
  82 #endif //WINDOWS
  83 
  84     bool start_launcher(int argc, TCHAR* argv[]) {
  85         bool result = false;
  86         bool parentProcess = true;
  87 
  88         // Platform must be initialize first.
  89         Platform& platform = Platform::GetInstance();
  90 
  91         try {
  92             platform.SetAppCDSState(cdsOn);
  93 
  94             for (int index = 0; index < argc; index++) {
  95                 TString argument = argv[index];
  96 
  97                 if (argument == _T("-Xappcds:generatecache")) {
  98                     platform.SetAppCDSState(cdsGenCache);
  99                 }
 100                 else if (argument == _T("-Xappcds:off")) {
 101                     platform.SetAppCDSState(cdsNone);
 102                 }
 103                 else if (argument == _T("-Xapp:child")) {
 104                     parentProcess = false;
 105                 }
 106 #ifdef DEBUG
 107 //TODO There appears to be a compiler bug on Mac overloading ShowResponseMessage. Investigate.
 108                 else if (argument == _T("-nativedebug")) {
 109                     if (platform.ShowResponseMessage(_T("Test"),
 110                                                      TString(_T("Would you like to debug?\n\nProcessID: ")) +
 111                                                      PlatformString(platform.GetProcessID()).toString()) == mrOK) {
 112                         while (platform.IsNativeDebuggerPresent() == false) {
 113                         }
 114                     }
 115                 }
 116 #endif //DEBUG
 117             }
 118 
 119             // Package must be initialized after Platform is fully initialized.
 120             Package& package = Package::GetInstance();
 121             Macros::Initialize();
 122             package.SetCommandLineArguments(argc, argv);
 123             platform.SetCurrentDirectory(package.GetPackageAppDirectory());
 124 
 125             switch (platform.GetAppCDSState()) {
 126                 case cdsGenCache: {
 127                         TString cacheDirectory = package.GetAppCDSCacheDirectory();
 128 
 129                         if (FilePath::DirectoryExists(cacheDirectory) == false) {
 130                             FilePath::CreateDirectory(cacheDirectory, true);
 131                         }
 132                         else {
 133                             TString cacheFileName = package.GetAppCDSCacheFileName();
 134 
 135                             if (FilePath::FileExists(cacheFileName) == true) {
 136                                 FilePath::DeleteFile(cacheFileName);
 137                             }
 138                         }
 139 
 140 
 141                         break;
 142                     }
 143 
 144                 case cdsAuto: {
 145                     TString cacheFileName = package.GetAppCDSCacheFileName();
 146 
 147                     if (parentProcess == true && FilePath::FileExists(cacheFileName) == false) {
 148                         AutoFreePtr<Process> process = platform.CreateProcess();
 149                         std::vector<TString> args;
 150                         args.push_back(_T("-Xappcds:generatecache"));
 151                         args.push_back(_T("-Xapp:child"));
 152                         process->Execute(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(cdsNone);
 158                             package.Clear();
 159                             package.Initialize();
 160                         }
 161                     }
 162 
 163                     break;
 164                 }
 165 
 166                 case cdsNone:
 167                 case cdsOn: {
 168                     break;
 169                 }
 170             }
 171 
 172             // Validation
 173             {
 174                 switch (platform.GetAppCDSState()) {
 175                     case cdsOn:
 176                     case cdsAuto: {
 177                             TString cacheFileName = package.GetAppCDSCacheFileName();
 178 
 179                             if (FilePath::FileExists(cacheFileName) == false) {
 180                                 Messages& messages = Messages::GetInstance();
 181                                 TString message = PlatformString::Format(messages.GetMessage(APPCDS_CACHE_FILE_NOT_FOUND), cacheFileName.data());
 182                                 throw FileNotFoundException(message);
 183                             }
 184                             break;
 185                         }
 186                 }
 187             }
 188 
 189             TString logFileName = FilePath::IncludeTrailingSeparater(platform.GetAppDataDirectory().data()) + _T("log.txt");
 190 
 191             // Run App
 192             result = RunVM();
 193         }
 194         catch (FileNotFoundException &e) {
 195             platform.ShowMessage(e.GetMessage());
 196         }
 197 
 198         return result;
 199     }
 200 
 201     void stop_launcher() {
 202     }
 203 }