Skip to content

Commit 645c443

Browse files
committed
Find and use Java from JAVA_HOME env vars, if available
1 parent ed311bf commit 645c443

File tree

1 file changed

+62
-24
lines changed

1 file changed

+62
-24
lines changed

src/interceptors/jvm.ts

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,78 @@
11
import * as _ from 'lodash';
2+
import * as path from 'path';
23

34
import { Interceptor } from '.';
45

56
import { HtkConfig } from '../config';
67
import { spawnToResult, waitForExit } from '../process-management';
78
import { OVERRIDE_JAVA_AGENT } from './terminal/terminal-env-overrides';
89
import { reportError } from '../error-tracking';
9-
import { commandExists } from '../util';
10+
import { commandExists, delay } from '../util';
1011

1112
type JvmTarget = { pid: string, name: string, interceptedByProxy: number | undefined };
1213

1314
// Check that Java is present, and that it's compatible with agent attachment:
14-
const isJavaAvailable = commandExists('java').then(async (isAvailable) => {
15-
if (!isAvailable) return false;
16-
17-
const result = await spawnToResult(
18-
'java', [
19-
'-Djdk.attach.allowAttachSelf=true', // Required for self-test
20-
'-jar', OVERRIDE_JAVA_AGENT,
21-
'self-test'
22-
]
23-
);
24-
25-
// If Java is present, but it's not working, we report it. Hoping that this will hunt
26-
// down some specific incompatibilities that we can better work around/detect.
27-
if (result.exitCode !== 0) {
28-
console.log(result.stdout);
29-
console.log(result.stderr);
30-
throw new Error(`JVM attach not available, exited with ${result.exitCode}`);
15+
const javaBinPromise: Promise<string | false> = (async () => {
16+
const javaBinPaths = [
17+
// Check what Java binaries might exist:
18+
...(!!process.env.JAVA_HOME // $JAVA_HOME/bin/java is preferable
19+
? [path.join(process.env.JAVA_HOME!!, 'bin', 'java')]
20+
: []
21+
),
22+
...(await commandExists('java') // but any other Java in $PATH will do
23+
? ['java']
24+
: []
25+
)
26+
];
27+
28+
// Run a self test in parallel with each of them:
29+
const javaTestResults = await Promise.all(javaBinPaths.map(async (possibleJavaBin) => ({
30+
javaBin: possibleJavaBin,
31+
output: await testJavaBin(possibleJavaBin)
32+
})))
33+
34+
// Use the first Java in the list that succeeds:
35+
const bestJava = javaTestResults.filter(({ output }) =>
36+
output.exitCode === 0
37+
)[0];
38+
39+
if (javaTestResults.length && !bestJava) {
40+
// If some Java is present, but none are working, we report the failures. Hoping that this will hunt
41+
// down some specific incompatibilities that we can better work around/detect.
42+
javaTestResults.forEach((testResult) => {
43+
console.log(`Running ${testResult.javaBin}:`);
44+
console.log(testResult.output.stdout);
45+
console.log(testResult.output.stderr);
46+
});
47+
48+
throw new Error(`JVM attach not available, exited with ${javaTestResults[0].output.exitCode}`);
49+
} else if (bestJava) {
50+
return bestJava.javaBin;
3151
} else {
32-
return true;
52+
// No Java available anywhere - we just give up
53+
return false;
3354
}
34-
}).catch((e) => {
35-
// This is expected to happen occasionally, e.g. when using Java 8 (which
36-
// doesn't support the VM attachment APIs we need).
55+
})().catch((e) => {
3756
reportError(e);
3857
return false;
3958
});
4059

60+
function testJavaBin(possibleJavaBin: string) {
61+
return Promise.race([
62+
spawnToResult(
63+
possibleJavaBin, [
64+
'-Djdk.attach.allowAttachSelf=true', // Required for self-test
65+
'-jar', OVERRIDE_JAVA_AGENT,
66+
'self-test'
67+
]
68+
),
69+
// Time out permanently after 30 seconds - this only runs once max anyway
70+
delay(30000).then(() => {
71+
throw new Error(`Java bin test for ${possibleJavaBin} timed out`);
72+
})
73+
]);
74+
}
75+
4176
export class JvmInterceptor implements Interceptor {
4277
readonly id = 'attach-jvm';
4378
readonly version = '1.0.1';
@@ -49,7 +84,7 @@ export class JvmInterceptor implements Interceptor {
4984
constructor(private config: HtkConfig) { }
5085

5186
async isActivable(): Promise<boolean> {
52-
return await isJavaAvailable;
87+
return !!await javaBinPromise;
5388
}
5489

5590
isActive(proxyPort: number | string) {
@@ -79,8 +114,11 @@ export class JvmInterceptor implements Interceptor {
79114
private targetsPromise: Promise<JvmTarget[]> | undefined;
80115

81116
private async getTargets(): Promise<JvmTarget[]> {
117+
const javaBin = await javaBinPromise;
118+
if (!javaBin) throw new Error("Attach activated but no Java available");
119+
82120
const listTargetsOutput = await spawnToResult(
83-
'java', [
121+
javaBin, [
84122
'-jar', OVERRIDE_JAVA_AGENT,
85123
'list-targets'
86124
]

0 commit comments

Comments
 (0)