11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT license.
33
4- // based on https://github.com/redhat-developer/vscode-java/blob/fcbe9204638610ce773cedaef445f19032a785cb /src/requirements.ts
4+ // based on https://github.com/redhat-developer/vscode-java/blob/c4cdbed1190fc705c364179dab525645acf03898 /src/requirements.ts
55
66import * as fse from "fs-extra" ;
7+ import { findRuntimes , getRuntime , getSources , IJavaRuntime , JAVAC_FILENAME , JAVA_FILENAME } from 'jdk-utils' ;
78import * as path from "path" ;
89import * as vscode from "vscode" ;
9- import { env } from " vscode" ;
10+ import { env , workspace } from ' vscode' ;
1011
1112const expandHomeDir = require ( "expand-home-dir" ) ;
1213const REQUIRED_JDK_VERSION = 11 ;
13- import { findJavaHomes , getJavaVersion , JavaRuntime } from "./findJavaRuntime" ;
1414
15-
16- const isWindows = process . platform . indexOf ( "win" ) === 0 ;
17- const JAVAC_FILENAME = "javac" + ( isWindows ? ".exe" : "" ) ;
18-
19- export async function resolveRequirements ( ) : Promise < any > {
15+ export async function resolveRequirements ( ) : Promise < {
16+ tooling_jre : string | undefined ; // Used to launch Java extension.
17+ tooling_jre_version : number ;
18+ java_home : string | undefined ; // Used as default project JDK.
19+ java_version : number ;
20+ } > {
21+ const javaExtPath : string | undefined = vscode . extensions . getExtension ( "redhat.java" ) ?. extensionPath
22+ let toolingJre : string | undefined = await findEmbeddedJRE ( javaExtPath ) ;
23+ let toolingJreVersion : number = await getMajorVersion ( toolingJre ) ;
2024 return new Promise ( async ( resolve , reject ) => {
2125 let source : string ;
2226 let javaVersion : number = 0 ;
23- let javaHome = await checkJavaPreferences ( ) ;
24- if ( javaHome ) {
25- // java.home explictly specified
27+ let javaHome = checkJavaPreferences ( ) ;
28+ if ( ! toolingJre && javaHome ) { // "java.home" setting is only used for the universal version.
2629 source = `java.home variable defined in ${ env . appName } settings` ;
27- javaHome = expandHomeDir ( javaHome ) as string ;
30+ javaHome = expandHomeDir ( javaHome ) ;
2831 if ( ! await fse . pathExists ( javaHome ) ) {
2932 invalidJavaHome ( reject , `The ${ source } points to a missing or inaccessible folder (${ javaHome } )` ) ;
30- } else if ( ! await fse . pathExists ( path . resolve ( javaHome , " bin" , JAVAC_FILENAME ) ) ) {
33+ } else if ( ! await fse . pathExists ( path . resolve ( javaHome , ' bin' , JAVAC_FILENAME ) ) ) {
3134 let msg : string ;
3235 if ( await fse . pathExists ( path . resolve ( javaHome , JAVAC_FILENAME ) ) ) {
3336 msg = `'bin' should be removed from the ${ source } (${ javaHome } )` ;
@@ -36,36 +39,117 @@ export async function resolveRequirements(): Promise<any> {
3639 }
3740 invalidJavaHome ( reject , msg ) ;
3841 }
39- javaVersion = await getJavaVersion ( javaHome ) ;
42+ javaVersion = await getMajorVersion ( javaHome ) ;
43+ toolingJre = javaHome ;
44+ toolingJreVersion = javaVersion ;
4045 } else {
41- // java.home not specified, search valid JDKs from env.JAVA_HOME, env.PATH, Registry(Window), Common directories
42- const javaRuntimes = await findJavaHomes ( ) ;
43- const validJdks = javaRuntimes . filter ( r => r . version >= REQUIRED_JDK_VERSION ) ;
44- if ( validJdks . length > 0 ) {
45- sortJdksBySource ( validJdks ) ;
46- javaHome = validJdks [ 0 ] . home ;
47- javaVersion = validJdks [ 0 ] . version ;
46+ // java.home not specified, search valid JDKs from env.JAVA_HOME, env.PATH, SDKMAN, jEnv, jabba, Common directories
47+ const javaRuntimes = await findRuntimes ( { checkJavac : true , withVersion : true , withTags : true } ) ;
48+ if ( ! toolingJre ) { // universal version
49+ // as latest version as possible.
50+ sortJdksByVersion ( javaRuntimes ) ;
51+ const validJdks = javaRuntimes . filter ( r => r . version && r . version . major >= REQUIRED_JDK_VERSION ) ;
52+ if ( validJdks . length > 0 ) {
53+ sortJdksBySource ( validJdks ) ;
54+ javaHome = validJdks [ 0 ] . homedir ;
55+ javaVersion = validJdks [ 0 ] . version ?. major ?? 0 ;
56+ toolingJre = javaHome ;
57+ toolingJreVersion = javaVersion ;
58+ }
59+ } else { // pick a default project JDK/JRE
60+ /**
61+ * For legacy users, we implicitly following the order below to
62+ * set a default project JDK during initialization:
63+ * java.home > env.JDK_HOME > env.JAVA_HOME > env.PATH
64+ *
65+ * We'll keep it for compatibility.
66+ */
67+ if ( javaHome && ( await getRuntime ( javaHome ) !== undefined ) ) {
68+ const runtime = await getRuntime ( javaHome , { withVersion : true } ) ;
69+ if ( runtime ) {
70+ javaHome = runtime . homedir ;
71+ javaVersion = runtime . version ?. major ?? 0 ;
72+ }
73+ } else if ( javaRuntimes . length ) {
74+ sortJdksBySource ( javaRuntimes ) ;
75+ javaHome = javaRuntimes [ 0 ] . homedir ;
76+ javaVersion = javaRuntimes [ 0 ] . version ?. major ?? 0 ;
77+ } else if ( javaHome = ( await findDefaultRuntimeFromSettings ( ) ?? "" ) ) {
78+ javaVersion = await getMajorVersion ( javaHome ) ;
79+ } else {
80+ /**
81+ * Originally it was:
82+ * invalidJavaHome(reject, "Please download and install a JDK to compile your project. You can configure your projects with different JDKs by the setting ['java.configuration.runtimes'](https://github.com/redhat-developer/vscode-java/wiki/JDK-Requirements#java.configuration.runtimes)");
83+ *
84+ * here we focus on tooling jre, so we swallow the error.
85+ *
86+ */
87+ }
4888 }
4989 }
5090
51- if ( javaVersion < REQUIRED_JDK_VERSION ) {
52- let message = `Java ${ REQUIRED_JDK_VERSION } or more recent is required by the Java language support (redhat.java) extension.` ;
53- if ( javaHome ) {
54- message += `(Current JDK: ${ javaHome } )` ;
55- }
56- invalidJavaHome ( reject , message ) ;
91+ if ( ! toolingJre || toolingJreVersion < REQUIRED_JDK_VERSION ) {
92+ // For universal version, we still require users to install a qualified JDK to run Java extension.
93+ invalidJavaHome ( reject , `Java ${ REQUIRED_JDK_VERSION } or more recent is required to run the Java extension. Please download and install a recent JDK. You can still compile your projects with older JDKs by configuring ['java.configuration.runtimes'](https://github.com/redhat-developer/vscode-java/wiki/JDK-Requirements#java.configuration.runtimes)` ) ;
5794 }
5895
59- resolve ( { java_home : javaHome , java_version : javaVersion } ) ;
96+ resolve ( {
97+ tooling_jre : toolingJre , // Used to launch Java extension.
98+ tooling_jre_version : toolingJreVersion ,
99+ java_home : javaHome , // Used as default project JDK.
100+ java_version : javaVersion ,
101+ } ) ;
60102 } ) ;
61103}
62104
63- function sortJdksBySource ( jdks : JavaRuntime [ ] ) {
64- const rankedJdks = jdks as Array < JavaRuntime & { rank : number } > ;
65- const sources = [ "env.JDK_HOME" , "env.JAVA_HOME" , "env.PATH" ] ;
105+ async function findEmbeddedJRE ( javaExtPath ?: string ) : Promise < string | undefined > {
106+ if ( ! javaExtPath ) {
107+ return undefined ;
108+ }
109+ const jreHome = path . join ( javaExtPath , "jre" ) ;
110+ if ( fse . existsSync ( jreHome ) && fse . statSync ( jreHome ) . isDirectory ( ) ) {
111+ const candidates = fse . readdirSync ( jreHome ) ;
112+ for ( const candidate of candidates ) {
113+ if ( fse . existsSync ( path . join ( jreHome , candidate , "bin" , JAVA_FILENAME ) ) ) {
114+ return path . join ( jreHome , candidate ) ;
115+ }
116+ }
117+ }
118+
119+ return ;
120+ }
121+
122+ async function findDefaultRuntimeFromSettings ( ) : Promise < string | undefined > {
123+ const runtimes = workspace . getConfiguration ( ) . get ( "java.configuration.runtimes" ) ;
124+ if ( Array . isArray ( runtimes ) && runtimes . length ) {
125+ let candidate : string | undefined ;
126+ for ( const runtime of runtimes ) {
127+ if ( ! runtime || typeof runtime !== 'object' || ! runtime . path ) {
128+ continue ;
129+ }
130+
131+ const jr = await getRuntime ( runtime . path ) ;
132+ if ( jr ) {
133+ candidate = jr . homedir ;
134+ }
135+
136+ if ( runtime . default ) {
137+ break ;
138+ }
139+ }
140+
141+ return candidate ;
142+ }
143+
144+ return undefined ;
145+ }
146+
147+ function sortJdksBySource ( jdks : IJavaRuntime [ ] ) {
148+ const rankedJdks = jdks as Array < IJavaRuntime & { rank : number } > ;
149+ const sources = [ "JDK_HOME" , "JAVA_HOME" , "PATH" ] ;
66150 for ( const [ index , source ] of sources . entries ( ) ) {
67151 for ( const jdk of rankedJdks ) {
68- if ( jdk . rank === undefined && jdk . sources . includes ( source ) ) {
152+ if ( jdk . rank === undefined && getSources ( jdk ) . includes ( source ) ) {
69153 jdk . rank = index ;
70154 }
71155 }
@@ -74,10 +158,26 @@ function sortJdksBySource(jdks: JavaRuntime[]) {
74158 rankedJdks . sort ( ( a , b ) => a . rank - b . rank ) ;
75159}
76160
161+ /**
162+ * Sort by major version in descend order.
163+ */
164+ function sortJdksByVersion ( jdks : IJavaRuntime [ ] ) {
165+ jdks . sort ( ( a , b ) => ( b . version ?. major ?? 0 ) - ( a . version ?. major ?? 0 ) ) ;
166+ }
167+
168+
77169function checkJavaPreferences ( ) {
78- return vscode . workspace . getConfiguration ( "java" ) . get < string > ( "home" ) ;
170+ return vscode . workspace . getConfiguration ( "java" ) . get < string > ( "home" ) ?? "" ;
79171}
80172
81173function invalidJavaHome ( reject : any , reason : string ) {
82174 reject ( new Error ( reason ) ) ;
83175}
176+
177+ async function getMajorVersion ( javaHome ?: string ) : Promise < number > {
178+ if ( ! javaHome ) {
179+ return 0 ;
180+ }
181+ const runtime = await getRuntime ( javaHome , { withVersion : true } ) ;
182+ return runtime ?. version ?. major || 0 ;
183+ }
0 commit comments