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 "tstrings.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 #if defined(_WIN32) && !defined(_WIN64)
37 #define LAUNCH_FUNC "_JLI_Launch@56"
38 #else
39 #define LAUNCH_FUNC "JLI_Launch"
40 #endif
41
Jvm()42 Jvm::Jvm() {
43 LOG_TRACE(tstrings::any() << "Jvm(" << this << ")::Jvm()");
44 }
45
46
~Jvm()47 Jvm::~Jvm() {
48 LOG_TRACE(tstrings::any() << "Jvm(" << this << ")::~Jvm()");
49 }
50
51
initFromConfigFile(const CfgFile & cfgFile)52 Jvm& Jvm::initFromConfigFile(const CfgFile& cfgFile) {
53 const CfgFile::Properties& appOptions = cfgFile.getProperties(
54 SectionName::Application);
55
56 {
57 const CfgFile::Properties::const_iterator modulepath = appOptions.find(
58 PropertyName::modulepath);
59 if (modulepath != appOptions.end()) {
60 tstring_array::const_iterator it = modulepath->second.begin();
61 const tstring_array::const_iterator end = modulepath->second.end();
62 for (; it != end; ++it) {
63 addArgument(_T("--module-path"));
64 addArgument(*it);
65 };
66 }
67 }
68
69 {
70 const CfgFile::Properties::const_iterator classpath = appOptions.find(
71 PropertyName::classpath);
72 if (classpath != appOptions.end()) {
73 addArgument(_T("-classpath"));
74 addArgument(CfgFile::asPathList(*classpath));
75 }
76 }
77
78 {
79 const CfgFile::Properties::const_iterator splash = appOptions.find(
80 PropertyName::splash);
81 if (splash != appOptions.end()) {
82 const tstring splashPath = CfgFile::asString(*splash);
83 if (FileUtils::isFileExists(splashPath)) {
84 addArgument(_T("-splash:") + splashPath);
85 } else {
86 LOG_WARNING(tstrings::any()
87 << "Splash property ignored. File \""
88 << splashPath << "\" not found");
89 }
90 }
91 }
92
93 {
94 const CfgFile::Properties& section = cfgFile.getProperties(
95 SectionName::JavaOptions);
96 const CfgFile::Properties::const_iterator javaOptions = section.find(
97 PropertyName::javaOptions);
98 if (javaOptions != section.end()) {
99 tstring_array::const_iterator it = javaOptions->second.begin();
100 const tstring_array::const_iterator end = javaOptions->second.end();
101 for (; it != end; ++it) {
102 addArgument(*it);
103 };
104 }
105 }
106
107 {
108 addArgument(_T("-Djpackage.app-path=")
109 + SysInfo::getProcessModulePath());
110 }
111
112 // No validation of data in config file related to how Java app should be
113 // launched intentionally.
114 // Just read what is in config file and put on jvm's command line as is.
115
116 { // Run modular app
117 const CfgFile::Properties::const_iterator mainmodule = appOptions.find(
118 PropertyName::mainmodule);
119 if (mainmodule != appOptions.end()) {
120 addArgument(_T("-m"));
121 addArgument(CfgFile::asString(*mainmodule));
122 }
123 }
124
125 { // Run main class
126 const CfgFile::Properties::const_iterator mainclass = appOptions.find(
127 PropertyName::mainclass);
128 if (mainclass != appOptions.end()) {
129 addArgument(CfgFile::asString(*mainclass));
130 }
131 }
132
133 { // Run jar
134 const CfgFile::Properties::const_iterator mainjar = appOptions.find(
135 PropertyName::mainjar);
136 if (mainjar != appOptions.end()) {
137 addArgument(_T("-jar"));
138 addArgument(CfgFile::asString(*mainjar));
139 }
140 }
141
142 {
143 const CfgFile::Properties& section = cfgFile.getProperties(
144 SectionName::ArgOptions);
145 const CfgFile::Properties::const_iterator arguments = section.find(
146 PropertyName::arguments);
147 if (arguments != section.end()) {
148 tstring_array::const_iterator it = arguments->second.begin();
149 const tstring_array::const_iterator end = arguments->second.end();
150 for (; it != end; ++it) {
151 addArgument(*it);
152 };
153 }
154 }
155
156 return *this;
157 }
158
159
isWithSplash() const160 bool Jvm::isWithSplash() const {
161 tstring_array::const_iterator it = args.begin();
162 const tstring_array::const_iterator end = args.end();
163 for (; it != end; ++it) {
164 if (tstrings::startsWith(*it, _T("-splash:"))) {
165 return true;
166 }
167 }
168 return false;
169 }
170
171
172 namespace {
173
174 struct JvmlLauncherHandleCloser {
175 typedef JvmlLauncherHandle pointer;
176
operator ()__anon7de4a4d50111::JvmlLauncherHandleCloser177 void operator()(JvmlLauncherHandle h) {
178 jvmLauncherCloseHandle(jvmLauncherGetAPI(), h);
179 }
180 };
181
182 struct JvmlLauncherDataDeleter {
183 typedef JvmlLauncherData* pointer;
184
operator ()__anon7de4a4d50111::JvmlLauncherDataDeleter185 void operator()(JvmlLauncherData* ptr) {
186 free(ptr);
187 }
188 };
189
190 } // namespace
191
launch()192 void Jvm::launch() {
193 typedef std::unique_ptr<
194 JvmlLauncherHandle, JvmlLauncherHandleCloser> AutoJvmlLauncherHandle;
195
196 typedef std::unique_ptr<
197 JvmlLauncherData, JvmlLauncherDataDeleter> AutoJvmlLauncherData;
198
199 AutoJvmlLauncherHandle jlh(exportLauncher());
200
201 JvmlLauncherAPI* api = jvmLauncherGetAPI();
202
203 AutoJvmlLauncherData jld(jvmLauncherCreateJvmlLauncherData(api,
204 jlh.release()));
205
206 LOG_TRACE(tstrings::any() << "JVM library: \"" << jvmPath << "\"");
207
208 DllFunction<void*> func(Dll(jvmPath), LAUNCH_FUNC);
209
210 int exitStatus = jvmLauncherStartJvm(jld.get(), func.operator void*());
211
212 if (exitStatus != 0) {
213 JP_THROW("Failed to launch JVM");
214 }
215 }
216
217
218 namespace {
219
220 struct JliLaunchData {
221 std::string jliLibPath;
222 std::vector<std::string> args;
223
initJvmlLauncherData__anon7de4a4d50211::JliLaunchData224 int initJvmlLauncherData(JvmlLauncherData* ptr, int bufferSize) const {
225 int minimalBufferSize = initJvmlLauncherData(0);
226 if (minimalBufferSize <= bufferSize) {
227 initJvmlLauncherData(ptr);
228 }
229 return minimalBufferSize;
230 }
231
232 private:
initJvmlLauncherData__anon7de4a4d50211::JliLaunchData233 int initJvmlLauncherData(JvmlLauncherData* ptr) const {
234 // Store path to JLI library just behind JvmlLauncherData header.
235 char* curPtr = reinterpret_cast<char*>(ptr + 1);
236 {
237 const size_t count = sizeof(char)
238 * (jliLibPath.size() + 1 /* trailing zero */);
239 if (ptr) {
240 std::memcpy(curPtr, jliLibPath.c_str(), count);
241 ptr->jliLibPath = curPtr;
242 }
243 curPtr += count;
244 }
245
246 // Next write array of char* pointing to JLI lib arg strings.
247 if (ptr) {
248 ptr->jliLaunchArgv = reinterpret_cast<char**>(curPtr);
249 ptr->jliLaunchArgc = (int)args.size();
250 // Add terminal '0' arg.
251 ptr->jliLaunchArgv[ptr->jliLaunchArgc] = 0;
252 }
253
254 // Skip memory occupied by char* array.
255 curPtr += sizeof(char*) * (args.size() + 1 /* terminal '0' arg */);
256
257 // Store array of strings.
258 for (size_t i = 0; i != args.size(); i++) {
259 const size_t count = (args[i].size() + 1 /* trailing zero */);
260 if (ptr) {
261 std::memcpy(curPtr, args[i].c_str(), count);
262 ptr->jliLaunchArgv[i] = curPtr;
263 }
264 curPtr += count;
265 };
266
267 const size_t bufferSize = curPtr - reinterpret_cast<char*>(ptr);
268 if (ptr) {
269 LOG_TRACE(tstrings::any() << "Initialized " << bufferSize
270 << " bytes at " << ptr << " address");
271 } else {
272 LOG_TRACE(tstrings::any() << "Need " << bufferSize
273 << " bytes for JvmlLauncherData buffer");
274 }
275 return static_cast<int>(bufferSize);
276 }
277 };
278
279 } // namespace
280
exportLauncher() const281 JvmlLauncherHandle Jvm::exportLauncher() const {
282 std::unique_ptr<JliLaunchData> result(new JliLaunchData());
283
284 result->jliLibPath = tstrings::toUtf8(jvmPath);
285
286 #ifdef TSTRINGS_WITH_WCHAR
287 {
288 tstring_array::const_iterator it = args.begin();
289 const tstring_array::const_iterator end = args.end();
290 for (; it != end; ++it) {
291 result->args.push_back(tstrings::toACP(*it));
292 }
293 }
294 #else
295 result->args = args;
296 #endif
297
298 return result.release();
299 }
300
301
302 namespace {
303
closeHandle(JvmlLauncherHandle h)304 void closeHandle(JvmlLauncherHandle h) {
305 JP_TRY;
306
307 JliLaunchData* data = static_cast<JliLaunchData*>(h);
308 const std::unique_ptr<JliLaunchData> deleter(data);
309
310 JP_CATCH_ALL;
311 }
312
313
getJvmlLauncherDataSize(JvmlLauncherHandle h)314 int getJvmlLauncherDataSize(JvmlLauncherHandle h) {
315 JP_TRY;
316
317 const JliLaunchData* data = static_cast<const JliLaunchData*>(h);
318 return data->initJvmlLauncherData(0, 0);
319
320 JP_CATCH_ALL;
321
322 return -1;
323 }
324
325
initJvmlLauncherData(JvmlLauncherHandle h,void * ptr,int bufferSize)326 JvmlLauncherData* initJvmlLauncherData(JvmlLauncherHandle h,
327 void* ptr, int bufferSize) {
328 JP_TRY;
329
330 const JliLaunchData* data = static_cast<const JliLaunchData*>(h);
331 const int usedBufferSize = data->initJvmlLauncherData(
332 static_cast<JvmlLauncherData*>(ptr), bufferSize);
333 if (bufferSize <= usedBufferSize) {
334 return static_cast<JvmlLauncherData*>(ptr);
335 }
336
337 JP_CATCH_ALL;
338
339 return 0;
340 }
341
342 class Impl : public JvmlLauncherAPI {
343 public:
Impl()344 Impl() {
345 this->closeHandle = ::closeHandle;
346 this->getJvmlLauncherDataSize = ::getJvmlLauncherDataSize;
347 this->initJvmlLauncherData = ::initJvmlLauncherData;
348 }
349 } api;
350
351 } // namespace
352
353
354 extern "C" {
355
jvmLauncherGetAPI(void)356 JNIEXPORT JvmlLauncherAPI* jvmLauncherGetAPI(void) {
357 return &api;
358 }
359
360 } // extern "C"
361