java-caller 2.2.4 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/README.md +190 -176
- package/lib/cli.js +34 -34
- package/lib/index.js +7 -7
- package/lib/java-caller.js +416 -395
- package/package.json +75 -75
- package/CHANGELOG.md +0 -29
package/lib/java-caller.js
CHANGED
|
@@ -1,395 +1,416 @@
|
|
|
1
|
-
#! /usr/bin/env node
|
|
2
|
-
const debug = require("debug")("java-caller");
|
|
3
|
-
const fse = require("fs-extra");
|
|
4
|
-
const os = require("os");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const { spawn } = require("child_process");
|
|
7
|
-
const util = require("util");
|
|
8
|
-
const execPromise = util.promisify(require("child_process").exec);
|
|
9
|
-
|
|
10
|
-
class JavaCaller {
|
|
11
|
-
"use strict";
|
|
12
|
-
minimumJavaVersion = 8;
|
|
13
|
-
maximumJavaVersion;
|
|
14
|
-
javaType;
|
|
15
|
-
rootPath = ".";
|
|
16
|
-
|
|
17
|
-
jar;
|
|
18
|
-
classPath = ".";
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*
|
|
37
|
-
* @param {
|
|
38
|
-
* @param {string} [opts.
|
|
39
|
-
* @param {
|
|
40
|
-
* @param {
|
|
41
|
-
* @param {string} [opts.
|
|
42
|
-
* @param {
|
|
43
|
-
* @param {
|
|
44
|
-
* @param {string} [opts.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*
|
|
66
|
-
* @param {string} [
|
|
67
|
-
* @
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
console.log(`
|
|
236
|
-
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
this.
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
const debug = require("debug")("java-caller");
|
|
3
|
+
const fse = require("fs-extra");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { spawn } = require("child_process");
|
|
7
|
+
const util = require("util");
|
|
8
|
+
const execPromise = util.promisify(require("child_process").exec);
|
|
9
|
+
|
|
10
|
+
class JavaCaller {
|
|
11
|
+
"use strict";
|
|
12
|
+
minimumJavaVersion = 8;
|
|
13
|
+
maximumJavaVersion;
|
|
14
|
+
javaType;
|
|
15
|
+
rootPath = ".";
|
|
16
|
+
|
|
17
|
+
jar;
|
|
18
|
+
classPath = ".";
|
|
19
|
+
useAbsoluteClassPaths = false;
|
|
20
|
+
mainClass;
|
|
21
|
+
output = "none"; // can be none or console
|
|
22
|
+
status = null;
|
|
23
|
+
|
|
24
|
+
javaSupportDir;
|
|
25
|
+
javaExecutable = "java";
|
|
26
|
+
additionalJavaArgs = [];
|
|
27
|
+
commandJavaArgs = [];
|
|
28
|
+
|
|
29
|
+
javaHome;
|
|
30
|
+
javaBin;
|
|
31
|
+
|
|
32
|
+
prevPath;
|
|
33
|
+
prevJavaHome;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a JavaCaller instance
|
|
37
|
+
* @param {object} [opts] - Run options
|
|
38
|
+
* @param {string} [opts.jar] - Path to executable jar file
|
|
39
|
+
* @param {string | string[]} [opts.classPath] - If jar parameter is not set, classpath to use. Use : as separator (it will be converted if runned on Windows), or use a string array
|
|
40
|
+
* @param {boolean} [opts.useAbsoluteClassPaths] - Set to true if classpaths should not be based on the rootPath
|
|
41
|
+
* @param {string} [opts.mainClass] - If classPath set, main class to call
|
|
42
|
+
* @param {number} [opts.minimumJavaVersion] - Minimum java version to be used to call java command. If the java version found on machine is lower, java-caller will try to install and use the appropriate one
|
|
43
|
+
* @param {number} [opts.maximumJavaVersion] - Maximum java version to be used to call java command. If the java version found on machine is upper, java-caller will try to install and use the appropriate one
|
|
44
|
+
* @param {string} [opts.javaType] - jre or jdk (if not defined and installation is required, jre will be installed)
|
|
45
|
+
* @param {string} [opts.rootPath] - If classPath elements are not relative to the current folder, you can define a root path. You may use __dirname if you classes / jars are in your module folder
|
|
46
|
+
* @param {string} [opts.javaExecutable] - You can force to use a defined java executable, instead of letting java-caller find/install one
|
|
47
|
+
* @param {string} [opts.additionalJavaArgs] - Additional parameters for JVM that will be added in every JavaCaller instance runs
|
|
48
|
+
*/
|
|
49
|
+
constructor(opts) {
|
|
50
|
+
this.jar = opts.jar || this.jar;
|
|
51
|
+
this.classPath = opts.classPath || this.classPath;
|
|
52
|
+
this.useAbsoluteClassPaths = opts.useAbsoluteClassPaths || this.useAbsoluteClassPaths;
|
|
53
|
+
this.mainClass = opts.mainClass || this.mainClass;
|
|
54
|
+
this.minimumJavaVersion = opts.minimumJavaVersion || this.minimumJavaVersion;
|
|
55
|
+
this.maximumJavaVersion = opts.maximumJavaVersion || this.maximumJavaVersion;
|
|
56
|
+
this.javaType = opts.javaType || this.javaType;
|
|
57
|
+
this.rootPath = opts.rootPath || this.rootPath;
|
|
58
|
+
this.javaCallerSupportDir = `${os.homedir() + path.sep}.java-caller`;
|
|
59
|
+
this.javaExecutable = opts.javaExecutable || this.javaExecutable;
|
|
60
|
+
this.additionalJavaArgs = opts.additionalJavaArgs || this.additionalJavaArgs;
|
|
61
|
+
this.output = opts.output || this.output;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Runs java command of a JavaCaller instance
|
|
66
|
+
* @param {string[]} [userArguments] - Java command line arguments
|
|
67
|
+
* @param {object} [runOptions] - Run options
|
|
68
|
+
* @param {boolean} [runOptions.detached = false] - If set to true, node will node wait for the java command to be completed. In that case, childJavaProcess property will be returned, but stdout and stderr may be empty
|
|
69
|
+
* @param {number} [runOptions.waitForErrorMs = 500] - If detached is true, number of milliseconds to wait to detect an error before exiting JavaCaller run
|
|
70
|
+
* @param {string} [runOptions.cwd = .] - You can override cwd of spawn called by JavaCaller runner
|
|
71
|
+
* @param {string} [runOptions.javaArgs = []] - You can override cwd of spawn called by JavaCaller runner
|
|
72
|
+
* @return {Promise<{status:number, stdout:string, stderr:string, childJavaProcess:ChildProcess}>} - Command result (status, stdout, stderr, childJavaProcess)
|
|
73
|
+
*/
|
|
74
|
+
async run(userArguments, runOptions = {}) {
|
|
75
|
+
runOptions.detached = runOptions.detached || false;
|
|
76
|
+
runOptions.waitForErrorMs = runOptions.waitForErrorMs || 500;
|
|
77
|
+
runOptions.cwd = runOptions.cwd || process.cwd();
|
|
78
|
+
this.commandJavaArgs = (runOptions.javaArgs || []).concat(this.additionalJavaArgs);
|
|
79
|
+
|
|
80
|
+
let javaExe = this.javaExecutable;
|
|
81
|
+
if (javaExe.toLowerCase().includes(".exe") && !javaExe.includes(`'`)) {
|
|
82
|
+
// Java executable has been overridden by caller : use it
|
|
83
|
+
javaExe = `"${path.resolve(javaExe)}"`;
|
|
84
|
+
} else {
|
|
85
|
+
// Check if matching java version is present, install and update PATH if it is not
|
|
86
|
+
await this.manageJavaInstall();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const classPathStr = this.buildClasspathStr();
|
|
90
|
+
|
|
91
|
+
const javaArgs = this.buildArguments(classPathStr, (userArguments || []).concat(this.commandJavaArgs));
|
|
92
|
+
let stdout = "";
|
|
93
|
+
let stderr = "";
|
|
94
|
+
let child;
|
|
95
|
+
const prom = new Promise((resolve) => {
|
|
96
|
+
// Spawn java command line
|
|
97
|
+
debug(`Java command: ${javaExe} ${javaArgs.join(" ")}`);
|
|
98
|
+
const spawnOptions = {
|
|
99
|
+
detached: runOptions.detached,
|
|
100
|
+
cwd: javaExe === "java" ? runOptions.cwd : undefined,
|
|
101
|
+
env: Object.assign({}, process.env),
|
|
102
|
+
stdio: this.output === "console" ? "inherit" : runOptions.detached ? "ignore" : "pipe",
|
|
103
|
+
windowsHide: true,
|
|
104
|
+
windowsVerbatimArguments: true,
|
|
105
|
+
};
|
|
106
|
+
if (javaExe.includes(" ")) {
|
|
107
|
+
spawnOptions.shell = true;
|
|
108
|
+
}
|
|
109
|
+
child = spawn(javaExe, javaArgs, spawnOptions);
|
|
110
|
+
|
|
111
|
+
// Gather stdout and stderr if they must be returned
|
|
112
|
+
if (spawnOptions.stdio === "pipe") {
|
|
113
|
+
child.stdout.on("data", (data) => {
|
|
114
|
+
stdout += data;
|
|
115
|
+
});
|
|
116
|
+
child.stderr.on("data", (data) => {
|
|
117
|
+
stderr += data;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Catch error
|
|
122
|
+
child.on("error", (data) => {
|
|
123
|
+
this.status = 666;
|
|
124
|
+
stderr += "Java spawn error: " + data;
|
|
125
|
+
resolve();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Catch status code
|
|
129
|
+
child.on("close", (code) => {
|
|
130
|
+
this.status = code;
|
|
131
|
+
resolve();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Detach from main process in case detached === true
|
|
135
|
+
if (runOptions.detached) {
|
|
136
|
+
child.unref();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (runOptions.detached) {
|
|
141
|
+
// Detached mode: Just wait a little amount of time in case you want to check a command error
|
|
142
|
+
await new Promise((resolve) =>
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
resolve();
|
|
145
|
+
}, runOptions.waitForErrorMs)
|
|
146
|
+
);
|
|
147
|
+
} else {
|
|
148
|
+
// Not detached mode: wait for Promise to be resolved
|
|
149
|
+
await prom;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Build result
|
|
153
|
+
const result = {
|
|
154
|
+
status: this.status,
|
|
155
|
+
stdout: stdout,
|
|
156
|
+
stderr: stderr,
|
|
157
|
+
};
|
|
158
|
+
if (child) {
|
|
159
|
+
result.childJavaProcess = child;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Restore previous values of PATH & JAVA_HOME
|
|
163
|
+
process.env["PATH"] = this.prevPath || process.env["PATH"];
|
|
164
|
+
process.env["JAVA_HOME"] = this.prevJavaHome || process.env["JAVA_HOME"];
|
|
165
|
+
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Translate the classpath from a string or string array into a string
|
|
170
|
+
buildClasspathStr() {
|
|
171
|
+
let classPathList = [];
|
|
172
|
+
|
|
173
|
+
if (typeof this.classPath === "string") {
|
|
174
|
+
classPathList = this.classPath.split(":");
|
|
175
|
+
} else {
|
|
176
|
+
classPathList = this.classPath;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!this.useAbsoluteClassPaths) {
|
|
180
|
+
classPathList = classPathList.map((classPathElt) => path.resolve(this.rootPath + path.sep + classPathElt));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return classPathList.join(path.delimiter);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Set first java arguments, then jar || classpath, then jar/class user arguments
|
|
187
|
+
buildArguments(classPathStr, userArgs) {
|
|
188
|
+
let javaArgs = [];
|
|
189
|
+
let programArgs = [];
|
|
190
|
+
for (const arg of userArgs) {
|
|
191
|
+
if (arg.startsWith("-D") || arg.startsWith("-X") || this.commandJavaArgs.includes(arg)) {
|
|
192
|
+
javaArgs.push(arg);
|
|
193
|
+
} else {
|
|
194
|
+
programArgs.push(arg);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
let allArgs = [];
|
|
198
|
+
allArgs.push(...javaArgs);
|
|
199
|
+
if (this.jar) {
|
|
200
|
+
allArgs.push(...["-jar", os.platform() === "win32" ? `"${this.rootPath}/${this.jar}"` : `${this.rootPath}/${this.jar}`]);
|
|
201
|
+
} else {
|
|
202
|
+
allArgs.push(...["-cp", os.platform() === "win32" ? `"${classPathStr}"` : classPathStr, this.mainClass]);
|
|
203
|
+
}
|
|
204
|
+
allArgs.push(...programArgs);
|
|
205
|
+
return allArgs;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Install Java if the found java version is not matching the requirements
|
|
209
|
+
async manageJavaInstall() {
|
|
210
|
+
if (await this.getInstallInCache()) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const javaVersion = await this.getJavaVersion();
|
|
214
|
+
if (javaVersion === false || javaVersion < this.minimumJavaVersion || (this.maximumJavaVersion && javaVersion > this.maximumJavaVersion)) {
|
|
215
|
+
// Check if the appropriate version has already been installed
|
|
216
|
+
const { javaVersionHome = null, javaVersionBin = null } = await this.findJavaVersionHome();
|
|
217
|
+
if (javaVersionHome) {
|
|
218
|
+
// Matching java version has been found: use it
|
|
219
|
+
this.javaHome = javaVersionHome;
|
|
220
|
+
this.javaBin = javaVersionBin;
|
|
221
|
+
await this.addJavaInPath();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Inform user that the installation is pending
|
|
226
|
+
const requiredMsg =
|
|
227
|
+
this.minimumJavaVersion !== this.maximumJavaVersion
|
|
228
|
+
? `Java ${this.javaType ? this.javaType : "jre or jdk"} between ${this.minimumJavaVersion} and ${
|
|
229
|
+
this.maximumJavaVersion
|
|
230
|
+
} is required `
|
|
231
|
+
: `Java ${this.javaType ? this.javaType : "jre or jdk"} ${this.minimumJavaVersion} is required`;
|
|
232
|
+
console.log(requiredMsg);
|
|
233
|
+
const javaVersionToInstall = this.maximumJavaVersion || this.minimumJavaVersion;
|
|
234
|
+
const javaTypeToInstall = this.javaType || "jre";
|
|
235
|
+
console.log(`Installing Java ${javaTypeToInstall} ${javaVersionToInstall} in ${this.javaCallerSupportDir}...`);
|
|
236
|
+
|
|
237
|
+
// Create a directory for installing Java and ensure it contains a dummy package.json
|
|
238
|
+
await fse.ensureDir(this.javaCallerSupportDir, { mode: "0777" });
|
|
239
|
+
const packageJson = `${this.javaCallerSupportDir + path.sep}package.json`;
|
|
240
|
+
if (!fse.existsSync(packageJson)) {
|
|
241
|
+
const packageJsonContent = {
|
|
242
|
+
name: "java-caller-support",
|
|
243
|
+
version: "1.0.0",
|
|
244
|
+
description: "Java installations by java-caller (https://github.com/nvuillam/node-java-caller)",
|
|
245
|
+
};
|
|
246
|
+
await fse.writeFile(packageJson, JSON.stringify(packageJsonContent), "utf8");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Install appropriate java version using njre
|
|
250
|
+
const njre = require("njre");
|
|
251
|
+
const njreOptions = { type: javaTypeToInstall };
|
|
252
|
+
const prevRequireMainFilename = require.main.filename; // hack require.main.filename to njre installs java where we want
|
|
253
|
+
require.main.filename = packageJson;
|
|
254
|
+
const installDir = await njre.install(this.maximumJavaVersion || this.minimumJavaVersion, njreOptions);
|
|
255
|
+
require.main.filename = prevRequireMainFilename; // unhack require.main.filename
|
|
256
|
+
console.log(`Installed Java ${javaTypeToInstall} ${javaVersionToInstall} in ${installDir}...`);
|
|
257
|
+
|
|
258
|
+
// Call again this method: now matching java version will be found :)
|
|
259
|
+
return await this.manageJavaInstall();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Get matching version in java-caller cache
|
|
264
|
+
async getInstallInCache() {
|
|
265
|
+
if (globalThis.JAVA_CALLER_VERSIONS_CACHE != null) {
|
|
266
|
+
for (const { version, file, java_home, java_bin } of globalThis.JAVA_CALLER_VERSIONS_CACHE) {
|
|
267
|
+
if (this.checkMatchingJavaVersion(version, file)) {
|
|
268
|
+
this.javaHome = java_home;
|
|
269
|
+
this.javaBin = java_bin;
|
|
270
|
+
await this.addJavaInPath();
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Returns system default java version
|
|
279
|
+
async getJavaVersion() {
|
|
280
|
+
try {
|
|
281
|
+
const { stderr } = await execPromise("java -version");
|
|
282
|
+
const match = /version "(.*?)"/.exec(stderr);
|
|
283
|
+
const parts = match[1].split(".");
|
|
284
|
+
let join = ".";
|
|
285
|
+
let versionStr = "";
|
|
286
|
+
for (const v of parts) {
|
|
287
|
+
versionStr += v;
|
|
288
|
+
if (join !== null) {
|
|
289
|
+
versionStr += join;
|
|
290
|
+
join = null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
versionStr = versionStr.replace("_", "");
|
|
294
|
+
let versionNb = parseFloat(versionStr);
|
|
295
|
+
if (versionNb < 2) {
|
|
296
|
+
versionStr = versionStr.substring(2);
|
|
297
|
+
versionNb = parseFloat(versionStr[0] + "." + versionStr.substring(2));
|
|
298
|
+
}
|
|
299
|
+
debug(`Found default java version ${versionNb}`);
|
|
300
|
+
return versionNb;
|
|
301
|
+
} catch (e) {
|
|
302
|
+
debug(`Java not found: ${e.message}`);
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Browse locally installed java versions
|
|
308
|
+
// check if one matches with javaType , minimumJavaVersion and maximumJavaVersion
|
|
309
|
+
async findJavaVersionHome() {
|
|
310
|
+
let javaInstallsTopDir = this.javaCallerSupportDir + path.sep + "jre";
|
|
311
|
+
if (fse.existsSync(javaInstallsTopDir)) {
|
|
312
|
+
const dirContent = await fse.readdir(javaInstallsTopDir);
|
|
313
|
+
let cacheFutureItem;
|
|
314
|
+
let isFirst = true;
|
|
315
|
+
const dirContentFiltered = dirContent.filter((file) => {
|
|
316
|
+
if (!fse.statSync(path.join(javaInstallsTopDir, file)).isDirectory()) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
const versionMatches = file.includes("u")
|
|
320
|
+
? file.substring(0, file.indexOf("u")).match(/(\d+)/)
|
|
321
|
+
: file.substring(0, file.indexOf(".")).match(/(\d+)/);
|
|
322
|
+
const versionFound = parseInt(versionMatches[0], 10);
|
|
323
|
+
const isMatching = this.checkMatchingJavaVersion(versionFound, file);
|
|
324
|
+
if (isMatching && isFirst) {
|
|
325
|
+
cacheFutureItem = { versionFound, file };
|
|
326
|
+
isFirst = false;
|
|
327
|
+
}
|
|
328
|
+
return isMatching;
|
|
329
|
+
});
|
|
330
|
+
if (dirContentFiltered[0]) {
|
|
331
|
+
const javaVersionHome = javaInstallsTopDir + path.sep + dirContentFiltered[0];
|
|
332
|
+
const javaVersionBin = javaVersionHome + path.sep + this.getPlatformBinPath();
|
|
333
|
+
if (fse.existsSync(javaVersionBin)) {
|
|
334
|
+
debug(
|
|
335
|
+
`Found matching java bin: ${javaVersionBin} for ${this.javaType ? this.javaType : "jre or jdk"} ${this.minimumJavaVersion}${
|
|
336
|
+
this.maximumJavaVersion && this.maximumJavaVersion !== this.minimumJavaVersion ? " -> " + this.maximumJavaVersion : "+"
|
|
337
|
+
}`
|
|
338
|
+
);
|
|
339
|
+
this.addInCache(cacheFutureItem.versionFound, cacheFutureItem.file, javaVersionHome, javaVersionBin);
|
|
340
|
+
return { javaVersionHome, javaVersionBin };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return {};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
checkMatchingJavaVersion(versionFound, file) {
|
|
348
|
+
if (versionFound < this.minimumJavaVersion) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
if (this.maximumJavaVersion != null && versionFound > this.maximumJavaVersion) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
if (this.javaType === "jre" && !file.includes("jre")) {
|
|
355
|
+
return false;
|
|
356
|
+
} else if (this.javaType === "jdk" && file.includes("jre")) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Add java bin dir in PATH or JAVA_HOME
|
|
363
|
+
async addJavaInPath() {
|
|
364
|
+
this.prevPath = process.env["PATH"];
|
|
365
|
+
this.prevJavaHome = process.env["JAVA_HOME"];
|
|
366
|
+
// Add java-caller installed java version in PATH
|
|
367
|
+
if (this.javaBin) {
|
|
368
|
+
process.env["PATH"] = process.env["PATH"].includes(this.javaBin)
|
|
369
|
+
? process.env["PATH"]
|
|
370
|
+
: this.javaBin + path.delimiter + process.env["PATH"];
|
|
371
|
+
process.env["JAVA_HOME"] = this.javaHome;
|
|
372
|
+
}
|
|
373
|
+
// If JAVA_HOME is set, but not jdk or jre, add it in PATH
|
|
374
|
+
else if (process.env["JAVA_HOME"] && !process.env["PATH"].includes("jdk") && !process.env["PATH"].includes("jre")) {
|
|
375
|
+
process.env["PATH"] = process.env["PATH"].includes(process.env["JAVA_HOME"])
|
|
376
|
+
? process.env["PATH"]
|
|
377
|
+
: process.env["JAVA_HOME"] + path.sep + this.getPlatformBinPath() + path.delimiter + process.env["PATH"];
|
|
378
|
+
}
|
|
379
|
+
if (process.env["PATH"] !== this.prevPath) {
|
|
380
|
+
debug("Updated PATH with value: " + process.env["PATH"]);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
getPlatformBinPath() {
|
|
385
|
+
const platform = os.platform();
|
|
386
|
+
let binPath;
|
|
387
|
+
switch (platform) {
|
|
388
|
+
case "darwin":
|
|
389
|
+
binPath = `Contents${path.sep}Home${path.sep}bin`;
|
|
390
|
+
break;
|
|
391
|
+
case "win32":
|
|
392
|
+
binPath = "bin";
|
|
393
|
+
break;
|
|
394
|
+
case "linux":
|
|
395
|
+
binPath = "bin";
|
|
396
|
+
break;
|
|
397
|
+
default:
|
|
398
|
+
this.fail(`unsupported platform: ${platform}`);
|
|
399
|
+
}
|
|
400
|
+
return binPath;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
fail(reason) {
|
|
404
|
+
console.error(reason);
|
|
405
|
+
this.status = 666;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
addInCache(version, file, java_home, java_bin) {
|
|
409
|
+
if (globalThis.JAVA_CALLER_VERSIONS_CACHE == null) {
|
|
410
|
+
globalThis.JAVA_CALLER_VERSIONS_CACHE = [];
|
|
411
|
+
}
|
|
412
|
+
globalThis.JAVA_CALLER_VERSIONS_CACHE.push({ version, file, java_home, java_bin });
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
module.exports = { JavaCaller };
|