Skip to content

Commit 28000df

Browse files
authored
improve reliability of java home and version detection (#95)
* Introduce resolve_symlinks utility; normalize JAVA_HOME and use effective_java_home for cache keys; replace repeated symlink resolution with helper in java_find; remove redundant version-sorting * improve symlink path resolution robustness, and add rJava version caching tests. * fix tests * simplify effective Java home determination logic. * fix pkgdown * strenghen java version checks
1 parent 3944d1a commit 28000df

2 files changed

Lines changed: 175 additions & 10 deletions

File tree

R/java_env.R

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -500,17 +500,15 @@ java_check_version_cmd <- function(
500500
return(FALSE)
501501
}
502502

503-
# Extract Java version
504-
java_ver_string <- java_ver[[1]]
505-
matches <- regexec(
506-
'(openjdk|java) (version )?(\\\")?([0-9]{1,2})',
507-
java_ver_string
508-
)
509-
major_java_ver <- regmatches(java_ver_string, matches)[[1]][5]
503+
major_java_ver <- ._java_parse_version_output(java_ver)
510504

511-
# Fix 1 to 8, as Java 8 prints "1.8"
512-
if (major_java_ver == "1") {
513-
major_java_ver <- "8"
505+
if (isFALSE(major_java_ver)) {
506+
# Restore original environment
507+
if (!is.null(java_home)) {
508+
Sys.setenv(JAVA_HOME = old_java_home)
509+
Sys.setenv(PATH = old_path)
510+
}
511+
return(FALSE)
514512
}
515513

516514
# Restore original JAVA_HOME and PATH
@@ -593,3 +591,38 @@ java_get_home <- function() {
593591
}
594592
java_home
595593
}
594+
595+
#' Internal helper to parse java version output
596+
#' @noRd
597+
._java_parse_version_output <- function(java_ver) {
598+
# Handle null/empty input
599+
if (is.null(java_ver) || length(java_ver) == 0) {
600+
return(FALSE)
601+
}
602+
603+
# Extract Java version
604+
# Iterate over all lines to find the version string
605+
# This is needed because sometimes there is noise (e.g. "Picked up ...")
606+
major_java_ver <- NULL
607+
for (line in java_ver) {
608+
matches <- regexec(
609+
'^[[:space:]]*(openjdk|java)[[:space:]]+(version[[:space:]]+)?(")?([0-9]+)',
610+
line
611+
)
612+
if (matches[[1]][1] != -1) {
613+
major_java_ver <- regmatches(line, matches)[[1]][5]
614+
break
615+
}
616+
}
617+
618+
if (is.null(major_java_ver)) {
619+
return(FALSE)
620+
}
621+
622+
# Fix 1 to 8, as Java 8 prints "1.8"
623+
if (major_java_ver == "1") {
624+
major_java_ver <- "8"
625+
}
626+
627+
return(major_java_ver)
628+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Extract internal function for testing
2+
._java_parse_version_output <- getFromNamespace(
3+
"._java_parse_version_output",
4+
"rJavaEnv"
5+
)
6+
7+
test_that("._java_parse_version_output handles single-line standard output", {
8+
output <- c('openjdk version "21" 2023-09-19')
9+
expect_equal(._java_parse_version_output(output), "21")
10+
11+
output <- c('java version "1.8.0_381"')
12+
expect_equal(._java_parse_version_output(output), "8")
13+
})
14+
15+
test_that("._java_parse_version_output handles multi-line standard output", {
16+
output <- c(
17+
'openjdk version "21.0.1" 2023-10-17 LTS',
18+
'OpenJDK Runtime Environment Corretto-21.0.1.12.1 (build 21.0.1+12-LTS)',
19+
'OpenJDK 64-Bit Server VM Corretto-21.0.1.12.1 (build 21.0.1+12-LTS, mixed mode, sharing)'
20+
)
21+
expect_equal(._java_parse_version_output(output), "21")
22+
})
23+
24+
test_that("._java_parse_version_output handles noisy first line (regression test)", {
25+
output <- c(
26+
'Picked up _JAVA_OPTIONS: -Djava.awt.headless=true',
27+
'openjdk version "21" 2023-09-19'
28+
)
29+
expect_equal(._java_parse_version_output(output), "21")
30+
})
31+
32+
test_that("._java_parse_version_output handles timeout/error inputs", {
33+
expect_false(._java_parse_version_output(character(0)))
34+
expect_false(._java_parse_version_output(NULL))
35+
})
36+
37+
test_that("._java_parse_version_output handles output with no version", {
38+
output <- c("Some random output", "No version here")
39+
expect_false(._java_parse_version_output(output))
40+
})
41+
test_that("._java_parse_version_output handles real-world SDKMAN outputs", {
42+
# 21.0.2-open (OpenJDK 21)
43+
expect_equal(
44+
._java_parse_version_output(c(
45+
'openjdk version "21.0.8" 2025-07-15 LTS',
46+
'OpenJDK Runtime Environment Temurin-21.0.8+9 (build 21.0.8+9-LTS)',
47+
'OpenJDK 64-Bit Server VM Temurin-21.0.8+9 (build 21.0.8+9-LTS, mixed mode, sharing)'
48+
)),
49+
"21"
50+
)
51+
52+
# 17.0.10-tem (Temurin 17)
53+
expect_equal(
54+
._java_parse_version_output(c(
55+
'openjdk version "17.0.10" 2024-01-16',
56+
'OpenJDK Runtime Environment Temurin-17.0.10+7 (build 17.0.10+7)',
57+
'OpenJDK 64-Bit Server VM Temurin-17.0.10+7 (build 17.0.10+7, mixed mode)'
58+
)),
59+
"17"
60+
)
61+
62+
# 8.0.402-amzn (Corretto 8)
63+
expect_equal(
64+
._java_parse_version_output(c(
65+
'openjdk version "1.8.0_402"',
66+
'OpenJDK Runtime Environment Corretto-8.402.07.1 (build 1.8.0_402-b07)',
67+
'OpenJDK 64-Bit Server VM Corretto-8.402.07.1 (build 25.402-b07, mixed mode)'
68+
)),
69+
"8"
70+
)
71+
72+
# 11.0.22-ms (Microsoft 11)
73+
expect_equal(
74+
._java_parse_version_output(c(
75+
'openjdk version "11.0.22" 2024-01-16 LTS',
76+
'OpenJDK Runtime Environment Microsoft-8909545 (build 11.0.22+7-LTS)',
77+
'OpenJDK 64-Bit Server VM Microsoft-8909545 (build 11.0.22+7-LTS, mixed mode)'
78+
)),
79+
"11"
80+
)
81+
82+
# 22-open (OpenJDK 22)
83+
expect_equal(
84+
._java_parse_version_output(c(
85+
'openjdk version "22" 2024-03-19',
86+
'OpenJDK Runtime Environment (build 22+36-2370)',
87+
'OpenJDK 64-Bit Server VM (build 22+36-2370, mixed mode, sharing)'
88+
)),
89+
"22"
90+
)
91+
92+
# 17.0.10-zulu (Zulu 17)
93+
expect_equal(
94+
._java_parse_version_output(c(
95+
'openjdk version "17.0.10" 2024-01-16 LTS',
96+
'OpenJDK Runtime Environment Zulu17.48+15-CA (build 17.0.10+7-LTS)',
97+
'OpenJDK 64-Bit Server VM Zulu17.48+15-CA (build 17.0.10+7-LTS, mixed mode, sharing)'
98+
)),
99+
"17"
100+
)
101+
102+
# Issue #81 Regression Case: OpenJDK 17 (2021 release) without minor version
103+
# "openjdk version \"17\" 2021-09-14"
104+
expect_equal(
105+
._java_parse_version_output(c(
106+
'openjdk version "17" 2021-09-14',
107+
'OpenJDK Runtime Environment (build 17+35-2724)',
108+
'OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)'
109+
)),
110+
"17"
111+
)
112+
113+
# Amazon Corretto 8
114+
expect_equal(
115+
._java_parse_version_output(c(
116+
'openjdk version "1.8.0_402"',
117+
'OpenJDK Runtime Environment Corretto-8.402.07.1 (build 1.8.0_402-b07)',
118+
'OpenJDK 64-Bit Server VM Corretto-8.402.07.1 (build 25.402-b07, mixed mode)'
119+
)),
120+
"8"
121+
)
122+
123+
# Amazon Corretto 11 (simulated based on typical format)
124+
expect_equal(
125+
._java_parse_version_output(c(
126+
'openjdk version "11.0.12" 2021-07-20 LTS',
127+
'OpenJDK Runtime Environment Corretto-11.0.12.7.1 (build 11.0.12+7-LTS)',
128+
'OpenJDK 64-Bit Server VM Corretto-11.0.12.7.1 (build 11.0.12+7-LTS, mixed mode)'
129+
)),
130+
"11"
131+
)
132+
})

0 commit comments

Comments
 (0)