From 2a711c1ef3ec0862ba92ddb0c69e77905d756fa3 Mon Sep 17 00:00:00 2001 From: Julian Chang Date: Sun, 9 Aug 2026 16:18:31 +0700 Subject: [PATCH] actually properly check for AWT --- ...ca338b95637d43d9d067b974327f7720637b.json} | 16 ++++- ...55acbae41a67925b6b179b82dea1758d621a.json} | 16 ++++- ...38b743a7cd033fdef023c4b3b93827248be1.json} | 16 ++++- ...1ddd9904cc25f34bef3c16f29cfb141158f0.json} | 18 +++++- packages/oneclient_core/src/java_store.rs | 2 + .../20260809120000_java_probe_version.sql | 1 + packages/oneclient_db/src/dao/java.rs | 18 +++--- packages/oneclient_db/src/models/java.rs | 1 + .../oneclient_java/assets/java/JavaInfo.class | Bin 1716 -> 2874 bytes .../oneclient_java/assets/java/JavaInfo.java | 46 ++++++++++++++ packages/oneclient_java/src/checker.rs | 32 +++++++++- packages/oneclient_java/src/data.rs | 1 + packages/oneclient_java/src/error.rs | 14 ++++- packages/oneclient_java/src/service.rs | 56 +++++++++++++++++- 14 files changed, 215 insertions(+), 22 deletions(-) rename .sqlx/{query-0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba.json => query-0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b.json} (77%) rename .sqlx/{query-33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b.json => query-14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a.json} (77%) rename .sqlx/{query-f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2.json => query-331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1.json} (78%) rename .sqlx/{query-010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc.json => query-f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0.json} (66%) create mode 100644 packages/oneclient_db/migrations/20260809120000_java_probe_version.sql diff --git a/.sqlx/query-0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba.json b/.sqlx/query-0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b.json similarity index 77% rename from .sqlx/query-0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba.json rename to .sqlx/query-0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b.json index 5da3a9d7..3ce34f5c 100644 --- a/.sqlx/query-0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba.json +++ b/.sqlx/query-0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n FROM java_versions\n ORDER BY major DESC, version DESC\n ", + "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n FROM java_versions\n ORDER BY major DESC, version DESC\n ", "describe": { "columns": [ { @@ -68,6 +68,17 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "0d9fb5d2fa8a788770e946241ea2f62f4b794c0fdda3ccbde49384689911aeba" + "hash": "0c3651cf175cb900c2466f0e97beca338b95637d43d9d067b974327f7720637b" } diff --git a/.sqlx/query-33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b.json b/.sqlx/query-14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a.json similarity index 77% rename from .sqlx/query-33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b.json rename to .sqlx/query-14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a.json index caaad113..e50e191f 100644 --- a/.sqlx/query-33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b.json +++ b/.sqlx/query-14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tSELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n\t\tFROM java_versions\n\t\tWHERE major = ?\n\t\tORDER BY version DESC\n\t\tLIMIT 1\n\t\t", + "query": "\n\t\tSELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n\t\tFROM java_versions\n\t\tWHERE major = ?\n\t\tORDER BY version DESC\n\t\tLIMIT 1\n\t\t", "describe": { "columns": [ { @@ -68,6 +68,17 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "33771b7003e64c8f19d15d663de773afa568d626c60a37bbc32cc932746ad49b" + "hash": "14ab205c5b8d4c1c435814e4b7a155acbae41a67925b6b179b82dea1758d621a" } diff --git a/.sqlx/query-f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2.json b/.sqlx/query-331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1.json similarity index 78% rename from .sqlx/query-f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2.json rename to .sqlx/query-331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1.json index 27530fbf..23c27b31 100644 --- a/.sqlx/query-f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2.json +++ b/.sqlx/query-331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n FROM java_versions\n WHERE absolute_path = ?\n ", + "query": "\n SELECT absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n FROM java_versions\n WHERE absolute_path = ?\n ", "describe": { "columns": [ { @@ -68,6 +68,17 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "f5992c9cec6a6d7015cda4c04b463bd5fd9c42ac38b434fa8a0c0fe4f7517cf2" + "hash": "331bee3da454f39a8a2cc8ed4e5338b743a7cd033fdef023c4b3b93827248be1" } diff --git a/.sqlx/query-010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc.json b/.sqlx/query-f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0.json similarity index 66% rename from .sqlx/query-010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc.json rename to .sqlx/query-f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0.json index 33514fa6..044b3eb0 100644 --- a/.sqlx/query-010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc.json +++ b/.sqlx/query-f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tINSERT INTO java_versions (absolute_path, major, version, vendor, os_arch, is_jdk)\n\t\tVALUES (?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(absolute_path) DO UPDATE SET\n\t\t\tmajor = excluded.major,\n\t\t\tversion = excluded.version,\n\t\t\tvendor = excluded.vendor,\n\t\t\tos_arch = excluded.os_arch,\n\t\t\tis_jdk = excluded.is_jdk\n\t\tRETURNING absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\"\n\t\t", + "query": "\n\t\tINSERT INTO java_versions (absolute_path, major, version, vendor, os_arch, is_jdk, probe_version)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(absolute_path) DO UPDATE SET\n\t\t\tmajor = excluded.major,\n\t\t\tversion = excluded.version,\n\t\t\tvendor = excluded.vendor,\n\t\t\tos_arch = excluded.os_arch,\n\t\t\tis_jdk = excluded.is_jdk,\n\t\t\tprobe_version = excluded.probe_version\n\t\tRETURNING absolute_path, major, version, vendor, os_arch, is_jdk as \"is_jdk: bool\", probe_version\n\t\t", "describe": { "columns": [ { @@ -68,10 +68,21 @@ "name": "is_jdk" } } + }, + { + "name": "probe_version", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "java_versions", + "name": "probe_version" + } + } } ], "parameters": { - "Right": 6 + "Right": 7 }, "nullable": [ false, @@ -79,8 +90,9 @@ false, false, false, + false, false ] }, - "hash": "010f5dcda59ec32eaad1b9c3e2e9807f547a1376372c8b201653a36d6dd14cbc" + "hash": "f5dbcf7798afb495efe228a4ba7d1ddd9904cc25f34bef3c16f29cfb141158f0" } diff --git a/packages/oneclient_core/src/java_store.rs b/packages/oneclient_core/src/java_store.rs index 656e3306..eebcb6a9 100644 --- a/packages/oneclient_core/src/java_store.rs +++ b/packages/oneclient_core/src/java_store.rs @@ -25,6 +25,7 @@ fn row_to_runtime(row: JavaVersionRow) -> JavaRuntime { vendor: JavaVendor::from_str(&row.vendor).unwrap_or(JavaVendor::Other(row.vendor)), os_arch: row.os_arch, is_jdk: row.is_jdk, + probe_version: row.probe_version.try_into().unwrap_or(0), } } @@ -39,6 +40,7 @@ impl JavaStore for SqlJavaStore { &runtime.vendor.to_string(), &runtime.os_arch, runtime.is_jdk, + runtime.probe_version, ) .await .map_err(StoreError::new)?; diff --git a/packages/oneclient_db/migrations/20260809120000_java_probe_version.sql b/packages/oneclient_db/migrations/20260809120000_java_probe_version.sql new file mode 100644 index 00000000..0b03f787 --- /dev/null +++ b/packages/oneclient_db/migrations/20260809120000_java_probe_version.sql @@ -0,0 +1 @@ +ALTER TABLE `java_versions` ADD COLUMN `probe_version` INTEGER NOT NULL DEFAULT 0; diff --git a/packages/oneclient_db/src/dao/java.rs b/packages/oneclient_db/src/dao/java.rs index 98d79183..4138c1f0 100644 --- a/packages/oneclient_db/src/dao/java.rs +++ b/packages/oneclient_db/src/dao/java.rs @@ -3,6 +3,7 @@ use sqlx::SqlitePool; use crate::DbError; use crate::models::JavaVersionRow; +#[allow(clippy::too_many_arguments)] pub async fn insert( pool: &SqlitePool, absolute_path: &str, @@ -11,19 +12,21 @@ pub async fn insert( vendor: &str, os_arch: &str, is_jdk: bool, + probe_version: u32, ) -> Result { sqlx::query_as!( JavaVersionRow, r#" - INSERT INTO java_versions (absolute_path, major, version, vendor, os_arch, is_jdk) - VALUES (?, ?, ?, ?, ?, ?) + INSERT INTO java_versions (absolute_path, major, version, vendor, os_arch, is_jdk, probe_version) + VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(absolute_path) DO UPDATE SET major = excluded.major, version = excluded.version, vendor = excluded.vendor, os_arch = excluded.os_arch, - is_jdk = excluded.is_jdk - RETURNING absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool" + is_jdk = excluded.is_jdk, + probe_version = excluded.probe_version + RETURNING absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool", probe_version "#, absolute_path, i64::from(major), @@ -31,6 +34,7 @@ pub async fn insert( vendor, os_arch, is_jdk, + i64::from(probe_version), ) .fetch_one(pool) .await @@ -44,7 +48,7 @@ pub async fn get_by_path( let row = sqlx::query_as!( JavaVersionRow, r#" - SELECT absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool" + SELECT absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool", probe_version FROM java_versions WHERE absolute_path = ? "#, @@ -63,7 +67,7 @@ pub async fn get_latest_by_major( let row = sqlx::query_as!( JavaVersionRow, r#" - SELECT absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool" + SELECT absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool", probe_version FROM java_versions WHERE major = ? ORDER BY version DESC @@ -94,7 +98,7 @@ pub async fn list_all(pool: &SqlitePool) -> Result, DbError> let rows = sqlx::query_as!( JavaVersionRow, r#" - SELECT absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool" + SELECT absolute_path, major, version, vendor, os_arch, is_jdk as "is_jdk: bool", probe_version FROM java_versions ORDER BY major DESC, version DESC "#, diff --git a/packages/oneclient_db/src/models/java.rs b/packages/oneclient_db/src/models/java.rs index 246cee22..54c11188 100644 --- a/packages/oneclient_db/src/models/java.rs +++ b/packages/oneclient_db/src/models/java.rs @@ -8,4 +8,5 @@ pub struct JavaVersionRow { pub vendor: String, pub os_arch: String, pub is_jdk: bool, + pub probe_version: i64, } diff --git a/packages/oneclient_java/assets/java/JavaInfo.class b/packages/oneclient_java/assets/java/JavaInfo.class index a76328e46746057ec3b50f07ea64f60a1d0b8e8c..259d2f251a7322d0c373dd52a654c9475b3f4f05 100644 GIT binary patch literal 2874 zcma)8+jA3D9R3cw$!^2a(uDxwCLmx_pp}XhX+fYAEKM&q6>L#(n{L~sNwy^08{!QS zFL=L$S5V}oFXBw;=*WyOKKS6D;H&?F&ZzjE-NdF)XPikkd(Q9v{l4Eh`T6hfZUWeW zs}U@KqCqtfLRet&No&liOK{+ZkEH+RHb56wMWZttg z0t*LiuQTV4*g0=fU~|=-KH_(7mC@}|15qqvrtT;cj|NJPTieON9>d#Kra{9Bf#tVn zwSUw}_uIJ$mP3(`RR&gL4U4x%M(k|Az_$ODMZryjj=KcJZs}>gfepBuPZ;y56Q3=)UZAoVx+ZIR&X}Di1Uhya zco4e;7BSeqaj!iP`Go4#(OAs*WdE?QzQ@2rQhlM!&CQHBY1x=^>@%<*O#-St;jq1G zRZILx1kE_8;gErc(IQYW+t)%><>Zq8ns6;PSRghB{1Op~*^E{L3A7RD0XNrfWo&`n z^8npjLcdZErRezkWLrnEm;O+759Qo(t1oTqc!W{hJb}t^!C8T<@liPyM+A1x6SX90 zVU$Ya6}HOo7i_I7yf!dHd689ci|BfQA_>YYp1DV3X6JWUQ2q5zcGBm186w?6jG7`e=C~ zJ?`62a;byFS<06=P6El8KzYGpLK@^<9TTMVZQ6NPQu?%kr{w%Z2AphvKxhq53mC<* zJxJE-c(%yxAy;+u}O*UHm?r|g*Hk42Po z5}dz>sZ$&~IrBkBi&mvl9&;ZSO0F5Rb9u*QLKiu>G7NXJ{Vv;}Ho5&IOGU!T+U=v6 zK0DVf2Y`YM`!BGpYMx6X&KX#g^sLlyn>FI=YcMoirk{d#5g$^iF_kV{QIVuOnoHTT zblS^Y<>gc(^TBquZ2>PmftB*|;+w{I-g#=2>2yoQk4cup19Zv8l`X;w?ktuv^ZMqQv+BD|l%89XqbsKFQQ{#UeW z276^K3m|K$^UIbnM6iM&+=WHh$W!Ma7W4d!pdCvw%q#RfmfbYqgo0hJ6vy&<{A?qnJ*z zM>U6&iEF5@HZv4mf>CX{b97EgoxIdB3s$3YXFQqMQrgVeY%?b>A*4+CZTd}}YGo;s zN|QLtBv0dc|2M$6p5*~GEM+^d&N{?#9~$^is}1#doaKzLpb74r=KFKJ=+0s{o1!csoitpp-_RCxa+d=-y>8qH*TR%ZluR&FO@a3kQ;UbU9$vB{gJH3Sem>;E#|K#?H wy%H}SSNUN6DVB`AqU#Xtz&)h*^Be01NQdKck4O$^ZZW literal 1716 zcma)6OLN;)6#lLyS+cA=s>W@cI6#3GC!}^;LZOM1hPn=UIEjf(i&LN|wvs5ZEsw0E z?Ve!)|AI|6*|f~K9vC{qrVD-!D_B4|SC)y>c)^T)bniXqeCIoldw>7u=NAB`u&pA7 zYa!^kuHpth2$7hQ@u3O{*SNT;;3NM1Sj8<&E0|G`QptRlAI>Sb%{TMh_Y)a+RHSj2 zi;Ro~6`x{J#*%_%1uF`&3RY#TG3e>VOnN1A_hD{hJ(t-iEM@Wx=O1MEj6)+?F={)> zyz5xCof(E;%BorJ9D@)azsDe^?Xt-*khN;&+Cg>8bPC2+g-*n(VbvHe#CxD{$G*I4 zl=fGRx@Rxr4dTV1>>7=9#b`7bBJs1~$2TbphA_9Xt97elIv$Zt@=TQ~J~K6MA2=m* zfz%XdnQ~aFZQBW+jD|PymWDANZe2qTpJ~_tC42GpRPP*Em9pt*DByDjHml(t?#uW> zLlG1%#O)KtBX^czu(M7SY`d~=xfnG$9}|XDWjyJ49dj|)ZGsT*={5Zr?ktUc`Oc1r(xMt_^`jNm2Ij~o9?b% zJ&m%ywP%*x)4)#}u307j9n;M@cHMN`Ck$75HREi5s2TPF;YHdi%TDHKY+M>1)2Q;{ zG3s@*R%W>RzwrDXkfY+-?FnQUijVgS+^*c|h*X|Bk6)pB^c*Le!&rRtEZ#|A(R^%C zLSlT05ADE7d;BTwr!slVtZmS^Fhoz$>rzn`^0w`)8N3IxJ@Vh|N~_zio1S-0NW+}S z*pCDEEL3&~5g z8>27drxVY?o_dCFlMH%?G+%fJ@A{^HK!8lTAB5C11Xd?s%n3Keh&c8mZcj$U8`Dx$ zYC)I|MuRVL`5DAtAQhh@7?qlkb%jJUNTP{Q6Y3EpG4-v;j$ZwCVyTC@rPe}U6a8(w zF696zTNwOLHQK^CPB;KgIDs%piFPuEp-_e@5f5VleORQkHJrmf)#5u0;d?}InXE5C zd<6}Xj4O=)sSbbRmW=o4_ZQfo1gG*F@8`cr`3J~ta6-`aCeF7obYDEq3ddfpGzt{) z$>*(rFC+RAwHKs!H$j1fkN4;iE3lmp5Rbg$pksi$@TJ;}st?O!PXS{S+S5 z4vq5aBRCEif}qZ`d5)>zDmqP_^5O$A`Bz*l3i@!qDC*ICQPM~9#h^Z#{|SNDdmSJa J34#nD`7dnNe^USe diff --git a/packages/oneclient_java/assets/java/JavaInfo.java b/packages/oneclient_java/assets/java/JavaInfo.java index b846ad7a..6822b944 100644 --- a/packages/oneclient_java/assets/java/JavaInfo.java +++ b/packages/oneclient_java/assets/java/JavaInfo.java @@ -1,3 +1,6 @@ +import java.io.File; +import java.util.Locale; + public final class JavaInfo { private static final String[] CHECKED_PROPERTIES = new String[] {"os.arch", "java.version", "java.vendor", "java.home"}; @@ -15,6 +18,7 @@ public final class JavaInfo { } System.out.println("java.awt=" + hasClass("java.awt.Toolkit")); + System.out.println("java.awt.natives=" + hasAwtNatives()); System.out.println("java.jdk=" + hasCompiler()); System.exit(returnCode); @@ -30,6 +34,48 @@ public final class JavaInfo { } } + private static boolean hasAwtNatives() { + try { + String os = System.getProperty("os.name", "").toLowerCase(Locale.ROOT); + String[] required; + + if (os.contains("win")) { + required = new String[] {"awt.dll", "fontmanager.dll"}; + } else if (os.contains("mac") || os.contains("darwin")) { + required = new String[] {"libawt_lwawt.dylib", "libfontmanager.dylib"}; + } else { + required = new String[] {"libawt_xawt.so", "libfontmanager.so"}; + } + + for (String name : required) { + if (!findNative(name)) { + return false; + } + } + + return true; + } catch (Throwable ignored) { + return false; + } + } + + private static boolean findNative(String name) { + File home = new File(System.getProperty("java.home", ".")); + File[] roots = new File[] { + new File(home, "lib"), + new File(home, "bin"), + new File(new File(home, "lib"), System.getProperty("os.arch", "")), + }; + + for (File root : roots) { + if (new File(root, name).isFile()) { + return true; + } + } + + return false; + } + private static boolean hasCompiler() { try { return javax.tools.ToolProvider.getSystemJavaCompiler() != null; diff --git a/packages/oneclient_java/src/checker.rs b/packages/oneclient_java/src/checker.rs index d9ba40b4..5c413389 100644 --- a/packages/oneclient_java/src/checker.rs +++ b/packages/oneclient_java/src/checker.rs @@ -26,7 +26,9 @@ pub async fn check_java_runtime(absolute_path: String) -> JavaResult = command @@ -66,10 +68,12 @@ pub async fn check_java_runtime(absolute_path: String) -> JavaResult JavaResult) -> bool { + probe_flag(info, "java.awt") && probe_flag(info, "java.awt.natives") +} + fn probe_flag(info: &HashMap, key: &str) -> bool { info.get(key) .is_some_and(|value| value.eq_ignore_ascii_case("true")) @@ -132,4 +140,22 @@ mod tests { assert!(probe_flag(&info(&[("java.awt", "TRUE")]), "java.awt")); assert!(!probe_flag(&info(&[("java.awt", "false")]), "java.awt")); } + + #[test] + fn awt_classes_without_their_natives_are_not_usable() { + assert!(!has_usable_awt(&info(&[ + ("java.awt", "true"), + ("java.awt.natives", "false"), + ]))); + } + + #[test] + fn awt_needs_both_halves() { + assert!(has_usable_awt(&info(&[ + ("java.awt", "true"), + ("java.awt.natives", "true"), + ]))); + assert!(!has_usable_awt(&info(&[("java.awt", "true")]))); + assert!(!has_usable_awt(&info(&[("java.awt.natives", "true")]))); + } } diff --git a/packages/oneclient_java/src/data.rs b/packages/oneclient_java/src/data.rs index d92a9962..236703be 100644 --- a/packages/oneclient_java/src/data.rs +++ b/packages/oneclient_java/src/data.rs @@ -13,6 +13,7 @@ pub struct JavaRuntime { pub vendor: JavaVendor, pub os_arch: String, pub is_jdk: bool, + pub probe_version: u32, } diff --git a/packages/oneclient_java/src/error.rs b/packages/oneclient_java/src/error.rs index 4a88060c..a26affa6 100644 --- a/packages/oneclient_java/src/error.rs +++ b/packages/oneclient_java/src/error.rs @@ -41,7 +41,9 @@ pub enum JavaError { #[error("selected path is not a valid java installation: {path}")] InvalidJavaPath { path: String }, - #[error("java installation at '{path}' is missing java.awt (the java.desktop module)")] + #[error( + "java installation at '{path}' has no usable java.awt (the java.desktop module or its native libraries are missing)" + )] MissingAwtSupport { path: String }, #[error("selected java is version {found}, but version {expected} is required")] @@ -61,6 +63,16 @@ pub enum JavaError { } impl JavaError { + #[must_use] + pub fn is_invalid_installation(&self) -> bool { + matches!( + self, + JavaError::InvalidJavaPath { .. } + | JavaError::MissingAwtSupport { .. } + | JavaError::ParseVersion { .. } + ) + } + /// Whether this is an expected outcome or the user's environment rather /// than a launcher bug /// Callers decide what to do with that (e.g. suppress error reporting) diff --git a/packages/oneclient_java/src/service.rs b/packages/oneclient_java/src/service.rs index 9f516e41..37ba938c 100644 --- a/packages/oneclient_java/src/service.rs +++ b/packages/oneclient_java/src/service.rs @@ -22,6 +22,10 @@ pub const JAVA_VENDOR_HINT: &str = "java-vendor"; pub const INSTALLABLE_MAJORS: &[u32] = &[8, 11, 16, 17, 18, 19, 20, 21, 22, 23]; +pub const PROBE_VERSION: u32 = 1; + +const _: () = assert!(PROBE_VERSION > 0); + enum JavaPromptAnswer { Download, PickFolder, @@ -68,7 +72,47 @@ impl JavaService { let Some(path) = java_path else { return Ok(None); }; - Ok(self.store.get_by_path(path).await?) + let Some(runtime) = self.store.get_by_path(path).await? else { + return Ok(None); + }; + self.revalidate(runtime).await + } + + #[tracing::instrument(level = "debug", skip(self))] + async fn revalidate(&self, runtime: JavaRuntime) -> JavaResult> { + if runtime.probe_version == PROBE_VERSION { + if Path::new(&runtime.absolute_path).is_file() { + return Ok(Some(runtime)); + } + + tracing::warn!( + path = %runtime.absolute_path, + "forgetting recorded Java runtime: the executable is gone" + ); + self.store.delete_by_path(&runtime.absolute_path).await?; + return Ok(None); + } + + match checker::check_java_runtime(runtime.absolute_path.clone()).await { + Ok(info) => Ok(Some( + self.persist(Path::new(&runtime.absolute_path), &info).await?, + )), + Err(err) if err.is_invalid_installation() => { + tracing::warn!( + path = %runtime.absolute_path, + "forgetting recorded Java runtime: {err}" + ); + self.store.delete_by_path(&runtime.absolute_path).await?; + Ok(None) + } + Err(err) => { + tracing::warn!( + path = %runtime.absolute_path, + "could not re-probe recorded Java runtime, trusting the record: {err}" + ); + Ok(Some(runtime)) + } + } } #[tracing::instrument(level = "debug", skip(self))] @@ -125,7 +169,14 @@ impl JavaService { auto_install: bool, progress: Option<&GroupedProgressSession>, ) -> JavaResult { - let recorded = self.store.latest_by_major(major).await?; + let recorded = loop { + let Some(runtime) = self.store.latest_by_major(major).await? else { + break None; + }; + if let Some(valid) = self.revalidate(runtime).await? { + break Some(valid); + } + }; if let Some(runtime) = &recorded && runtime.is_jdk @@ -314,6 +365,7 @@ impl JavaService { .unwrap_or_else(|_| JavaVendor::Other(info.vendor.clone())), os_arch: info.os_arch.clone(), is_jdk: info.is_jdk, + probe_version: PROBE_VERSION, }; Ok(self.store.upsert(&runtime).await?)