1 /*
   2  * Copyright (c) 2020, 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 <cstring>
  27 #include <jni.h>
  28 #include "JvmLauncher.h"
  29 #include "Log.h"
  30 #include "Dll.h"
  31 #include "CfgFile.h"
  32 #include "FileUtils.h"
  33 #include "Toolbox.h"
  34 #include "ErrorHandling.h"
  35 
  36 
  37 Jvm& Jvm::initFromConfigFile(const CfgFile& cfgFile) {
  38     const CfgFile::Properties& appOptions = cfgFile.getProperties(
  39             SectionName::Application);
  40 
  41     do {
  42         const CfgFile::Properties::const_iterator modulepath = appOptions.find(
  43                 PropertyName::modulepath);
  44         if (modulepath != appOptions.end()) {
  45             tstring_array::const_iterator it = modulepath->second.begin();
  46             const tstring_array::const_iterator end = modulepath->second.end();
  47             for (; it != end; ++it) {
  48                 addArgument(_T("--module-path"));
  49                 addArgument(*it);
  50             };
  51         }
  52     } while (0);
  53 
  54     do {
  55         const CfgFile::Properties::const_iterator classpath = appOptions.find(
  56                 PropertyName::classpath);
  57         if (classpath != appOptions.end()) {
  58             addArgument(_T("-classpath"));
  59             addArgument(CfgFile::asPathList(*classpath));
  60         }
  61     } while (0);
  62 
  63     do {
  64         const CfgFile::Properties::const_iterator splash = appOptions.find(
  65                 PropertyName::splash);
  66         if (splash != appOptions.end()) {
  67             const tstring splashPath = CfgFile::asString(*splash);
  68             if (FileUtils::isFileExists(splashPath)) {
  69                 addArgument(_T("-splash:") + splashPath);
  70             } else {
  71                 LOG_WARNING(tstrings::any()
  72                         << "Splash property ignored. File \""
  73                         << splashPath << "\" not found");
  74             }
  75         }
  76     } while (0);
  77 
  78     do {
  79         const CfgFile::Properties& section = cfgFile.getProperties(
  80                 SectionName::JavaOptions);
  81         const CfgFile::Properties::const_iterator javaOptions = section.find(
  82                 PropertyName::javaOptions);
  83         if (javaOptions != section.end()) {
  84             tstring_array::const_iterator it = javaOptions->second.begin();
  85             const tstring_array::const_iterator end = javaOptions->second.end();
  86             for (; it != end; ++it) {
  87                 addArgument(*it);
  88             };
  89         }
  90     } while (0);
  91 
  92     // No validation of data in config file related to how Java app should be
  93     // launched intentionally.
  94     // Just read what is in config file and put on jvm's command line as is.
  95 
  96     do { // Run modular app
  97         const CfgFile::Properties::const_iterator mainmodule = appOptions.find(
  98                 PropertyName::mainmodule);
  99         if (mainmodule != appOptions.end()) {
 100             addArgument(_T("-m"));
 101             addArgument(CfgFile::asString(*mainmodule));
 102         }
 103     } while (0);
 104 
 105     do { // Run main class
 106         const CfgFile::Properties::const_iterator mainclass = appOptions.find(
 107                 PropertyName::mainclass);
 108         if (mainclass != appOptions.end()) {
 109             addArgument(CfgFile::asString(*mainclass));
 110         }
 111     } while (0);
 112 
 113     do { // Run jar
 114         const CfgFile::Properties::const_iterator mainjar = appOptions.find(
 115                 PropertyName::mainjar);
 116         if (mainjar != appOptions.end()) {
 117             addArgument(_T("-jar"));
 118             addArgument(CfgFile::asString(*mainjar));
 119         }
 120     } while (0);
 121 
 122     do {
 123         const CfgFile::Properties& section = cfgFile.getProperties(
 124                 SectionName::ArgOptions);
 125         const CfgFile::Properties::const_iterator arguments = section.find(
 126                 PropertyName::arguments);
 127         if (arguments != section.end()) {
 128             tstring_array::const_iterator it = arguments->second.begin();
 129             const tstring_array::const_iterator end = arguments->second.end();
 130             for (; it != end; ++it) {
 131                 addArgument(*it);
 132             };
 133         }
 134     } while (0);
 135 
 136     return *this;
 137 }
 138 
 139 
 140 bool Jvm::isWithSplash() const {
 141     tstring_array::const_iterator it = args.begin();
 142     const tstring_array::const_iterator end = args.end();
 143     for (; it != end; ++it) {
 144         if (tstrings::startsWith(*it, _T("-splash:"))) {
 145             return true;
 146         }
 147     }
 148     return false;
 149 }
 150 
 151 
 152 namespace {
 153 void convertArgs(const std::vector<std::string>& args, std::vector<char*>& argv) {
 154     argv.reserve(args.size() + 1);
 155     argv.resize(0);
 156 
 157     std::vector<std::string>::const_iterator it = args.begin();
 158     const std::vector<std::string>::const_iterator end = args.end();
 159 
 160     for (; it != end; ++it) {
 161         argv.push_back(const_cast<char*>(it->c_str()));
 162     };
 163 
 164     // Add treminal '0'.
 165     argv.push_back(0);
 166 }
 167 } // namespace
 168 
 169 void Jvm::launch() {
 170     typedef int (JNICALL *LaunchFuncType)(int argc, char ** argv,
 171         int jargc, const char** jargv,
 172         int appclassc, const char** appclassv,
 173         const char* fullversion,
 174         const char* dotversion,
 175         const char* pname,
 176         const char* lname,
 177         jboolean javaargs,
 178         jboolean cpwildcard,
 179         jboolean javaw,
 180         jint ergo);
 181 
 182     std::vector<char*> argv;
 183 #ifdef TSTRINGS_WITH_WCHAR
 184     std::vector<std::string> mbcs_args;
 185     do {
 186         tstring_array::const_iterator it = args.begin();
 187         const tstring_array::const_iterator end = args.end();
 188         for (; it != end; ++it) {
 189             mbcs_args.push_back(tstrings::toACP(*it));
 190         }
 191     } while (0);
 192     convertArgs(mbcs_args, argv);
 193 #else
 194     convertArgs(args, argv);
 195 #endif
 196 
 197     // Don't count terminal '0'.
 198     const int argc = (int)argv.size() - 1;
 199 
 200     LOG_TRACE(tstrings::any() << "JVM library: \"" << jvmPath << "\"");
 201 
 202     DllFunction<LaunchFuncType> func(Dll(jvmPath), "JLI_Launch");
 203     int exitStatus = func(argc, argv.data(),
 204         0, 0,
 205         0, 0,
 206         "",
 207         "",
 208         "java",
 209         "java",
 210         JNI_FALSE,
 211         JNI_FALSE,
 212         JNI_FALSE,
 213         0);
 214 
 215     if (exitStatus != 0) {
 216         JP_THROW("Failed to launch JVM");
 217     }
 218 }