@@ -12,6 +12,8 @@ namespace Spawner.JavaManager
1212{
1313 public static class JavaRuntimeManager
1414 {
15+ private const string JavaRuntimesDirEnvVar = "SPAWNER_JAVA_RUNTIMES_DIR" ;
16+
1517 public static Dictionary < string , string > GetInstalledJavaVersions ( )
1618 {
1719 string javaInstancesLocation = GetJavaRuntimesLocation ( ) ;
@@ -85,29 +87,31 @@ public static async Task InstallNewJavaVersion(
8587 CancellationToken ct = default )
8688 {
8789 var installedJavaVersions = GetInstalledJavaVersions ( ) ;
88- if ( installedJavaVersions . ContainsKey ( javaVersion ) ) return ;
90+ if ( installedJavaVersions . TryGetValue ( javaVersion , out var existingJavaPath ) )
91+ {
92+ if ( ! string . IsNullOrWhiteSpace ( existingJavaPath ) && File . Exists ( existingJavaPath ) )
93+ return ;
94+
95+ installedJavaVersions . Remove ( javaVersion ) ;
96+ UpdateInstalledJavaVersions ( installedJavaVersions ) ;
97+ }
8998
9099 string os = GetAdoptiumOS ( ) ;
91100 string architecture = GetAdoptiumArchitecture ( ) ;
92101
93- string assetsUrl = $ "https://api.adoptium.net/v3/assets/latest/{ javaVersion } /hotspot?os={ os } &architecture={ architecture } &image_type=jre&vendor=eclipse";
94-
95- var assetsResult = await client . GetStringAsync ( assetsUrl , ct ) ;
96- JsonNode ? assets = JsonNode . Parse ( assetsResult ) ;
102+ string jreFinalDir = Path . Combine ( GetJavaRuntimesLocation ( ) , javaVersion ) ;
103+ var javaBinary = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? "java.exe" : "java" ;
104+ var javaBinaryPath = Path . Combine ( jreFinalDir , "bin" , javaBinary ) ;
97105
98- if ( assets == null || assets . AsArray ( ) . Count == 0 )
99- throw new Exception ( $ "No assets found for Java version { javaVersion } on { os } { architecture } ") ;
106+ var package = await GetLatestAdoptiumPackage ( client , javaVersion , os , architecture , ct )
107+ ?? throw new Exception ( $ "No usable Adoptium runtime found for Java version { javaVersion } on { os } { architecture } ") ;
100108
101- string downloadUrl = assets [ 0 ] ? [ "binary" ] ? [ "package" ] ? [ "link" ] ? . ToString ( )
102- ?? throw new Exception ( "Download link not found in assets." ) ;
103- long jreSize = assets [ 0 ] ? [ "binary" ] ? [ "package" ] ? [ "size" ] ? . GetValue < long > ( )
104- ?? throw new Exception ( "JRE size not found in assets." ) ;
105- string jreSha256 = assets [ 0 ] ? [ "binary" ] ? [ "package" ] ? [ "checksum" ] ? . ToString ( )
106- ?? throw new Exception ( "JRE SHA256 not found in assets." ) ;
107- string jreFileName = assets [ 0 ] ? [ "binary" ] ? [ "package" ] ? [ "name" ] ? . ToString ( ) ?? $ "jre-{ javaVersion } .zip";
109+ string downloadUrl = package . Link ;
110+ long jreSize = package . Size ;
111+ string jreSha256 = package . Checksum ;
112+ string jreFileName = package . Name ;
108113
109114 string jreDownloadPath = Path . Combine ( GetJavaRuntimesLocation ( ) , "jreZipDownloads" , javaVersion , jreFileName ) ;
110- string jreFinalDir = Path . Combine ( GetJavaRuntimesLocation ( ) , javaVersion ) ;
111115
112116 var progress = new Progress < ( long received , long ? total ) > ( p =>
113117 {
@@ -125,13 +129,14 @@ public static async Task InstallNewJavaVersion(
125129
126130 await Download . DownloadFile ( client , downloadUrl , jreDownloadPath , progress , jreSize , ( HashAlgorithmName . SHA256 , jreSha256 ) , ct ) ;
127131 ExtractJre ( jreDownloadPath , jreFinalDir ) ;
132+ if ( ! File . Exists ( javaBinaryPath ) )
133+ throw new FileNotFoundException ( $ "Java runtime extracted, but the launcher was not found at '{ javaBinaryPath } '.") ;
128134
129135 var downloadsDir = Path . Combine ( GetJavaRuntimesLocation ( ) , "jreZipDownloads" , javaVersion ) ;
130136 if ( Directory . Exists ( downloadsDir ) )
131137 Directory . Delete ( downloadsDir , true ) ;
132138
133- var javaBinary = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? "java.exe" : "java" ;
134- installedJavaVersions [ javaVersion ] = Path . Combine ( jreFinalDir , "bin" , javaBinary ) ;
139+ installedJavaVersions [ javaVersion ] = javaBinaryPath ;
135140 UpdateInstalledJavaVersions ( installedJavaVersions ) ;
136141 }
137142
@@ -206,13 +211,64 @@ private static void ExtractJre(string jreDownloadPath, string jreFinalDir)
206211
207212 public static string GetJavaRuntimesLocation ( )
208213 {
214+ var configured = ( Environment . GetEnvironmentVariable ( JavaRuntimesDirEnvVar ) ?? "" ) . Trim ( ) ;
215+ if ( configured . Length > 0 )
216+ {
217+ Directory . CreateDirectory ( configured ) ;
218+ return configured ;
219+ }
220+
209221 string appData = Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) ;
222+ if ( string . IsNullOrWhiteSpace ( appData ) )
223+ appData = Environment . GetFolderPath ( Environment . SpecialFolder . UserProfile ) ;
224+ if ( string . IsNullOrWhiteSpace ( appData ) )
225+ appData = AppContext . BaseDirectory ;
226+
210227 string javaInstancesLocation = Path . Combine ( appData , "Spawner" , "JavaRuntimes" ) ;
211228 if ( ! Directory . Exists ( javaInstancesLocation ) )
212229 Directory . CreateDirectory ( javaInstancesLocation ) ;
213230 return javaInstancesLocation ;
214231 }
215232
233+ private static async Task < AdoptiumPackage ? > GetLatestAdoptiumPackage (
234+ HttpClient client ,
235+ string javaVersion ,
236+ string os ,
237+ string architecture ,
238+ CancellationToken ct )
239+ {
240+ foreach ( var imageType in new [ ] { "jre" , "jdk" } )
241+ {
242+ string assetsUrl = $ "https://api.adoptium.net/v3/assets/latest/{ javaVersion } /hotspot?os={ os } &architecture={ architecture } &image_type={ imageType } &vendor=eclipse";
243+
244+ var assetsResult = await client . GetStringAsync ( assetsUrl , ct ) ;
245+ JsonNode ? assets = JsonNode . Parse ( assetsResult ) ;
246+ var first = assets ? . AsArray ( ) . FirstOrDefault ( ) ;
247+ if ( first is null ) continue ;
248+
249+ var pkg = first [ "binary" ] ? [ "package" ] ;
250+ var link = pkg ? [ "link" ] ? . ToString ( ) ;
251+ var checksum = pkg ? [ "checksum" ] ? . ToString ( ) ;
252+ var size = pkg ? [ "size" ] ? . GetValue < long > ( ) ;
253+ var name = pkg ? [ "name" ] ? . ToString ( ) ;
254+ if ( string . IsNullOrWhiteSpace ( link ) ||
255+ string . IsNullOrWhiteSpace ( checksum ) ||
256+ size is null ||
257+ size . Value <= 0 )
258+ {
259+ continue ;
260+ }
261+
262+ return new AdoptiumPackage (
263+ link ,
264+ checksum ,
265+ size . Value ,
266+ string . IsNullOrWhiteSpace ( name ) ? $ "java-{ javaVersion } -{ imageType } " : name ) ;
267+ }
268+
269+ return null ;
270+ }
271+
216272 private static void TryBackupCorruptJson ( string path )
217273 {
218274 try
@@ -243,6 +299,8 @@ private static void CopyDirectoryRecursive(string srcDir, string dstDir)
243299 CopyDirectoryRecursive ( sub , Path . Combine ( dstDir , name ) ) ;
244300 }
245301 }
302+
303+ private sealed record AdoptiumPackage ( string Link , string Checksum , long Size , string Name ) ;
246304 }
247305
248306 public sealed record JavaDownloadProgress (
0 commit comments