| /* |
| * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include <cstddef> |
| #include <cstring> |
| #include "tstrings.h" |
| #include "JvmLauncher.h" |
| #include "Log.h" |
| #include "Dll.h" |
| #include "CfgFile.h" |
| #include "FileUtils.h" |
| #include "Toolbox.h" |
| #include "ErrorHandling.h" |
| |
| Jvm::Jvm() { |
| LOG_TRACE(tstrings::any() << "Jvm(" << this << ")::Jvm()"); |
| } |
| |
| |
| Jvm::~Jvm() { |
| LOG_TRACE(tstrings::any() << "Jvm(" << this << ")::~Jvm()"); |
| } |
| |
| |
| Jvm& Jvm::initFromConfigFile(const CfgFile& cfgFile) { |
| const CfgFile::Properties& appOptions = cfgFile.getProperties( |
| SectionName::Application); |
| |
| { |
| const CfgFile::Properties::const_iterator modulepath = appOptions.find( |
| PropertyName::modulepath); |
| if (modulepath != appOptions.end()) { |
| tstring_array::const_iterator it = modulepath->second.begin(); |
| const tstring_array::const_iterator end = modulepath->second.end(); |
| for (; it != end; ++it) { |
| addArgument(_T("--module-path")); |
| addArgument(*it); |
| }; |
| } |
| } |
| |
| { |
| const CfgFile::Properties::const_iterator classpath = appOptions.find( |
| PropertyName::classpath); |
| if (classpath != appOptions.end()) { |
| addArgument(_T("-classpath")); |
| addArgument(CfgFile::asPathList(*classpath)); |
| } |
| } |
| |
| { |
| const CfgFile::Properties::const_iterator splash = appOptions.find( |
| PropertyName::splash); |
| if (splash != appOptions.end()) { |
| const tstring splashPath = CfgFile::asString(*splash); |
| if (FileUtils::isFileExists(splashPath)) { |
| addArgument(_T("-splash:") + splashPath); |
| } else { |
| LOG_WARNING(tstrings::any() |
| << "Splash property ignored. File \"" |
| << splashPath << "\" not found"); |
| } |
| } |
| } |
| |
| { |
| const CfgFile::Properties& section = cfgFile.getProperties( |
| SectionName::JavaOptions); |
| const CfgFile::Properties::const_iterator javaOptions = section.find( |
| PropertyName::javaOptions); |
| if (javaOptions != section.end()) { |
| tstring_array::const_iterator it = javaOptions->second.begin(); |
| const tstring_array::const_iterator end = javaOptions->second.end(); |
| for (; it != end; ++it) { |
| addArgument(*it); |
| }; |
| } |
| } |
| |
| { |
| addArgument(_T("-Djpackage.app-path=") |
| + SysInfo::getProcessModulePath()); |
| } |
| |
| // No validation of data in config file related to how Java app should be |
| // launched intentionally. |
| // Just read what is in config file and put on jvm's command line as is. |
| |
| { // Run modular app |
| const CfgFile::Properties::const_iterator mainmodule = appOptions.find( |
| PropertyName::mainmodule); |
| if (mainmodule != appOptions.end()) { |
| addArgument(_T("-m")); |
| addArgument(CfgFile::asString(*mainmodule)); |
| } |
| } |
| |
| { // Run main class |
| const CfgFile::Properties::const_iterator mainclass = appOptions.find( |
| PropertyName::mainclass); |
| if (mainclass != appOptions.end()) { |
| addArgument(CfgFile::asString(*mainclass)); |
| } |
| } |
| |
| { // Run jar |
| const CfgFile::Properties::const_iterator mainjar = appOptions.find( |
| PropertyName::mainjar); |
| if (mainjar != appOptions.end()) { |
| addArgument(_T("-jar")); |
| addArgument(CfgFile::asString(*mainjar)); |
| } |
| } |
| |
| { |
| const CfgFile::Properties& section = cfgFile.getProperties( |
| SectionName::ArgOptions); |
| const CfgFile::Properties::const_iterator arguments = section.find( |
| PropertyName::arguments); |
| if (arguments != section.end()) { |
| tstring_array::const_iterator it = arguments->second.begin(); |
| const tstring_array::const_iterator end = arguments->second.end(); |
| for (; it != end; ++it) { |
| addArgument(*it); |
| }; |
| } |
| } |
| |
| return *this; |
| } |
| |
| |
| bool Jvm::isWithSplash() const { |
| tstring_array::const_iterator it = args.begin(); |
| const tstring_array::const_iterator end = args.end(); |
| for (; it != end; ++it) { |
| if (tstrings::startsWith(*it, _T("-splash:"))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| namespace { |
| |
| struct JvmlLauncherHandleCloser { |
| typedef JvmlLauncherHandle pointer; |
| |
| void operator()(JvmlLauncherHandle h) { |
| jvmLauncherCloseHandle(jvmLauncherGetAPI(), h); |
| } |
| }; |
| |
| struct JvmlLauncherDataDeleter { |
| typedef JvmlLauncherData* pointer; |
| |
| void operator()(JvmlLauncherData* ptr) { |
| free(ptr); |
| } |
| }; |
| |
| } // namespace |
| |
| void Jvm::launch() { |
| typedef std::unique_ptr< |
| JvmlLauncherHandle, JvmlLauncherHandleCloser> AutoJvmlLauncherHandle; |
| |
| typedef std::unique_ptr< |
| JvmlLauncherData, JvmlLauncherDataDeleter> AutoJvmlLauncherData; |
| |
| AutoJvmlLauncherHandle jlh(exportLauncher()); |
| |
| JvmlLauncherAPI* api = jvmLauncherGetAPI(); |
| |
| AutoJvmlLauncherData jld(jvmLauncherCreateJvmlLauncherData(api, |
| jlh.release(), 0)); |
| |
| LOG_TRACE(tstrings::any() << "JVM library: \"" << jvmPath << "\""); |
| |
| DllFunction<void*> func(Dll(jvmPath), "JLI_Launch"); |
| |
| int exitStatus = jvmLauncherStartJvm(jld.get(), func.operator void*()); |
| |
| if (exitStatus != 0) { |
| JP_THROW("Failed to launch JVM"); |
| } |
| } |
| |
| |
| void Jvm::setEnvVariables() { |
| for (size_t i = 0; i != envVarNames.size(); i++) { |
| SysInfo::setEnvVariable(envVarNames.at(i), envVarValues.at(i)); |
| } |
| } |
| |
| |
| namespace { |
| |
| struct JliLaunchData { |
| std::string jliLibPath; |
| std::vector<std::string> args; |
| tstring_array envVarNames; |
| tstring_array envVarValues; |
| |
| int initJvmlLauncherData(JvmlLauncherData* ptr, int bufferSize) const { |
| int minimalBufferSize = initJvmlLauncherData(0); |
| if (minimalBufferSize <= bufferSize) { |
| initJvmlLauncherData(ptr); |
| } |
| return minimalBufferSize; |
| } |
| |
| private: |
| template <class T> |
| static char* copyStrings(const std::vector<T>& src, |
| JvmlLauncherData* ptr, const size_t offset, char* curPtr) { |
| char** strArray = 0; |
| if (ptr) { |
| strArray = *reinterpret_cast<char***>( |
| reinterpret_cast<char*>(ptr) + offset); |
| } |
| |
| for (size_t i = 0; i != src.size(); i++) { |
| const size_t count = (src[i].size() + 1 /* trailing zero */) |
| * sizeof(typename T::value_type); |
| if (ptr) { |
| std::memcpy(curPtr, src[i].c_str(), count); |
| strArray[i] = curPtr; |
| } |
| |
| curPtr += count; |
| }; |
| |
| return curPtr; |
| } |
| |
| int initJvmlLauncherData(JvmlLauncherData* ptr) const { |
| // Store path to JLI library just behind JvmlLauncherData header. |
| JvmlLauncherData dummy; |
| char* curPtr = reinterpret_cast<char*>((ptr ? ptr : &dummy) + 1); |
| { |
| const size_t count = sizeof(char) |
| * (jliLibPath.size() + 1 /* trailing zero */); |
| if (ptr) { |
| std::memcpy(curPtr, jliLibPath.c_str(), count); |
| ptr->jliLibPath = curPtr; |
| } |
| curPtr += count; |
| } |
| |
| // Write array of char* pointing to JLI lib arg strings. |
| if (ptr) { |
| ptr->jliLaunchArgv = reinterpret_cast<char**>(curPtr); |
| ptr->jliLaunchArgc = (int)args.size(); |
| // Add terminal '0' arg. |
| ptr->jliLaunchArgv[ptr->jliLaunchArgc] = 0; |
| } |
| // Skip memory occupied by JvmlLauncherData::jliLaunchArgv array. |
| curPtr += sizeof(char*) * (args.size() + 1 /* terminal '0' arg */); |
| // Store JLI lib arg strings. |
| curPtr = copyStrings(args, ptr, |
| offsetof(JvmlLauncherData, jliLaunchArgv), curPtr); |
| |
| // Write array of char* pointing to env variable name strings. |
| if (ptr) { |
| ptr->envVarNames = reinterpret_cast<TCHAR**>(curPtr); |
| ptr->envVarCount = (int)envVarNames.size(); |
| } |
| // Skip memory occupied by JvmlLauncherData::envVarNames array. |
| curPtr += sizeof(TCHAR*) * envVarNames.size(); |
| // Store env variable names. |
| curPtr = copyStrings(envVarNames, ptr, |
| offsetof(JvmlLauncherData, envVarNames), curPtr); |
| |
| // Write array of char* pointing to env variable value strings. |
| if (ptr) { |
| ptr->envVarValues = reinterpret_cast<TCHAR**>(curPtr); |
| } |
| // Skip memory occupied by JvmlLauncherData::envVarValues array. |
| curPtr += sizeof(TCHAR*) * envVarValues.size(); |
| // Store env variable values. |
| curPtr = copyStrings(envVarValues, ptr, |
| offsetof(JvmlLauncherData, envVarValues), curPtr); |
| |
| const size_t bufferSize = curPtr - reinterpret_cast<char*>(ptr ? ptr : &dummy); |
| if (ptr) { |
| LOG_TRACE(tstrings::any() << "Initialized " << bufferSize |
| << " bytes at " << ptr << " address"); |
| } else { |
| LOG_TRACE(tstrings::any() << "Need " << bufferSize |
| << " bytes for JvmlLauncherData buffer"); |
| } |
| return static_cast<int>(bufferSize); |
| } |
| }; |
| |
| void copyStringArray(const tstring_array& src, std::vector<std::string>& dst) { |
| #ifdef TSTRINGS_WITH_WCHAR |
| { |
| tstring_array::const_iterator it = src.begin(); |
| const tstring_array::const_iterator end = src.end(); |
| for (; it != end; ++it) { |
| dst.push_back(tstrings::toACP(*it)); |
| } |
| } |
| #else |
| dst = src; |
| #endif |
| } |
| |
| } // namespace |
| |
| JvmlLauncherHandle Jvm::exportLauncher() const { |
| std::unique_ptr<JliLaunchData> result(new JliLaunchData()); |
| |
| result->jliLibPath = tstrings::toUtf8(jvmPath); |
| |
| copyStringArray(args, result->args); |
| result->envVarNames = envVarNames; |
| result->envVarValues = envVarValues; |
| |
| return result.release(); |
| } |
| |
| |
| namespace { |
| |
| void closeHandle(JvmlLauncherHandle h) { |
| JP_TRY; |
| |
| JliLaunchData* data = static_cast<JliLaunchData*>(h); |
| const std::unique_ptr<JliLaunchData> deleter(data); |
| |
| JP_CATCH_ALL; |
| } |
| |
| |
| int getJvmlLauncherDataSize(JvmlLauncherHandle h) { |
| JP_TRY; |
| |
| const JliLaunchData* data = static_cast<const JliLaunchData*>(h); |
| return data->initJvmlLauncherData(0, 0); |
| |
| JP_CATCH_ALL; |
| |
| return -1; |
| } |
| |
| |
| JvmlLauncherData* initJvmlLauncherData(JvmlLauncherHandle h, |
| void* ptr, int bufferSize) { |
| JP_TRY; |
| |
| const JliLaunchData* data = static_cast<const JliLaunchData*>(h); |
| const int usedBufferSize = data->initJvmlLauncherData( |
| static_cast<JvmlLauncherData*>(ptr), bufferSize); |
| if (bufferSize <= usedBufferSize) { |
| return static_cast<JvmlLauncherData*>(ptr); |
| } |
| |
| JP_CATCH_ALL; |
| |
| return 0; |
| } |
| |
| class Impl : public JvmlLauncherAPI { |
| public: |
| Impl() { |
| this->closeHandle = ::closeHandle; |
| this->getJvmlLauncherDataSize = ::getJvmlLauncherDataSize; |
| this->initJvmlLauncherData = ::initJvmlLauncherData; |
| } |
| } api; |
| |
| } // namespace |
| |
| |
| extern "C" { |
| |
| JNIEXPORT JvmlLauncherAPI* jvmLauncherGetAPI(void) { |
| return &api; |
| } |
| |
| } // extern "C" |