blob: 83f45c2cdfe2903d473146a835a616b86ea5f461 [file] [edit]
/*
* 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"