Skip to content

Commit b69794a

Browse files
e-kotovCopilot
andauthored
feat: Implement java_ensure and refactor java_env_set (#92)
* add java_ensure, refactor package * increase system java timeout * Handle tryCatch errors and NULL default_java in java_find_system to avoid logical(0) assignment and crashes * tests: mock internal Java version and env implementations to avoid invoking real Java * fix tests for linux * fix tests on windows * Document java_check_version_* returning FALSE; add pkgdown entries for java_ensure and java_find_system; update live test to handle missing rJava (skip/alert) and adjust assertions/messages. * Restructure pkgdown articles into Getting Started/About/Advanced; add **/*.quarto_ipynb to vignettes/.gitignore * Add .use_cache support: add global_use_cache_param and man page, propagate .use_cache through java_ensure -> java_check_version_cmd/java_find_system, update man pages, add tests, and revise developer vignette/docs. * add scoped env functions * Respect quiet flag in java_env_set_session: suppress cli::cli_warn when quiet = TRUE (avoid warnings on libjvm missing/failed load) * Add documentation for '...' argument in with_java_env/local_java_env * Update R/java_ensure.R Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add with_rjava_env subprocess helper, add java_check.R and man pages, and update developer vignette * improve docs and test coverage * increase test coverage * fix tests * fix tests * fix tests * fix tests * add new functions to pkgdown * update tests * bump test coverage * add examples for java_find_system --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent bae39a3 commit b69794a

81 files changed

Lines changed: 6921 additions & 436 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.Rbuildignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@
3131
^\.config$
3232
^\.local$
3333
^\.jupyter$
34+
^[.]?air[.]toml$
35+
^\.vscode$

DESCRIPTION

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,20 @@ URL: https://github.com/e-kotov/rJavaEnv, https://www.ekotov.pro/rJavaEnv/
4545
BugReports: https://github.com/e-kotov/rJavaEnv/issues
4646
Depends:
4747
R (>= 4.0)
48-
Imports:
48+
Imports:
4949
checkmate,
5050
cli,
5151
curl,
5252
jsonlite,
53+
memoise,
5354
rlang,
54-
utils
55+
utils,
56+
withr
5557
Suggests:
58+
callr,
5659
quarto,
5760
rJava,
58-
testthat (>= 3.0.0),
59-
withr
61+
testthat (>= 3.0.0)
6062
VignetteBuilder:
6163
quarto
6264
Config/testthat/edition: 3

NAMESPACE

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,29 @@
22

33
export(java_build_env_set)
44
export(java_build_env_unset)
5+
export(java_check_compatibility)
56
export(java_check_version_cmd)
67
export(java_check_version_rjava)
78
export(java_clear)
89
export(java_download)
10+
export(java_ensure)
911
export(java_env_set)
1012
export(java_env_unset)
13+
export(java_find_system)
1114
export(java_get_home)
1215
export(java_install)
1316
export(java_list)
1417
export(java_quick_install)
18+
export(java_resolve)
1519
export(java_unpack)
1620
export(java_valid_versions)
21+
export(local_java_env)
1722
export(rje_consent)
1823
export(use_java)
24+
export(with_java_env)
25+
export(with_rjava_env)
1926
import(rlang)
27+
importFrom(memoise,cache_memory)
28+
importFrom(memoise,memoise)
2029
importFrom(utils,getFromNamespace)
2130
importFrom(utils,installed.packages)

NEWS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88

99
- New function `java_get_home()` to get currently set `JAVA_HOME`. This is just a shortcut to `Sys.getenv("JAVA_HOME")`, as it is faster to type and can be even faster then used with autocomplete.
1010

11+
## Performance Improvements
12+
13+
- **Automatic caching for version checks**: `java_check_version_cmd()` and `java_find_system()` are now memoised within the R session.
14+
- First call to `java_check_version_cmd()`: ~37ms
15+
- Subsequent calls: <1ms (123x faster)
16+
- Cache correctly handles version switching via `use_java()` by using JAVA_HOME as key
17+
- No manual caching needed in downstream packages - completely transparent
18+
19+
- Added `memoise` package dependency for session-scoped caching
20+
1121
## Improvements
1222

1323
- Download progress printing in `java_download()` respects the `quiet` argument now (internally passing the value to `curl::curl_download()`).

R/consent.R

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,29 @@
99
#'
1010
#' Alternatively, you can set the following \R option
1111
#' (especially useful for non-interactive R sessions):
12-
#'
12+
#'
1313
#' ```
1414
#' options(rJavaEnv.consent = TRUE)
1515
#' ```
1616
#' The function is based on the code of the `renv` package.
1717
#' Copyright 2023 Posit Software, PBC
1818
#' License: https://github.com/rstudio/renv/blob/main/LICENSE
19-
#'
19+
#'
2020
#' @param provided Logical indicating if consent is already provided.
2121
#' To provide consent in non-interactive \R sessions
2222
#' use `rJavaEnv::rje_consent(provided = TRUE)`. Default is `FALSE`.
23-
#'
23+
#'
2424
#' @return `TRUE` if consent is given, otherwise an error is raised.
25-
#'
25+
#'
2626
#' @export
2727
#' @examples
2828
#' \dontrun{
29-
#'
29+
#'
3030
#' # to provide consent and prevent other functions from interrupting to get the consent
3131
#' rje_consent(provided = TRUE)
3232
#' }
33-
#'
33+
#'
3434
rje_consent <- function(provided = FALSE) {
35-
3635
# Check if consent is already given via environment variable
3736
if (getOption("rJavaEnv.consent", default = FALSE)) {
3837
cli::cli_inform("Consent for using rJavaEnv has already been provided.")
@@ -41,7 +40,11 @@ rje_consent <- function(provided = FALSE) {
4140

4241
# Check if consent is already given via cache directory
4342
user_package_cache_path <- getOption("rJavaEnv.cache_path")
44-
user_package_cache_path <- normalizePath(user_package_cache_path, winslash = "/", mustWork = FALSE)
43+
user_package_cache_path <- normalizePath(
44+
user_package_cache_path,
45+
winslash = "/",
46+
mustWork = FALSE
47+
)
4548
if (dir.exists(user_package_cache_path)) {
4649
cli::cli_inform("Consent for using rJavaEnv has already been provided.")
4750
return(invisible(TRUE))
@@ -55,7 +58,7 @@ rje_consent <- function(provided = FALSE) {
5558

5659
# Request user consent if not already provided
5760
if (!provided) {
58-
response <- readline(prompt = "Your response: (yes/no) ")
61+
response <- rje_readline(prompt = "Your response: (yes/no) ")
5962
provided <- tolower(response) %in% c("y", "yes", "yes.")
6063
}
6164

@@ -78,11 +81,10 @@ rje_consent <- function(provided = FALSE) {
7881
#' The function is based on the code of the `renv` package.
7982
#' Copyright 2023 Posit Software, PBC
8083
#' License: https://github.com/rstudio/renv/blob/main/LICENSE
81-
#'
84+
#'
8285
#' @return `TRUE` if consent is verified, otherwise an error is raised.
8386
#' @keywords internal
8487
rje_consent_check <- function() {
85-
8688
# Check if explicit consent is given
8789
if (getOption("rJavaEnv.consent", FALSE)) {
8890
return(TRUE)
@@ -110,7 +112,7 @@ rje_consent_check <- function() {
110112

111113

112114
#' Helper for clean env var check
113-
#'
115+
#'
114116
#' #' The function is based on the code of the `renv` package.
115117
#' Copyright 2023 Posit Software, PBC
116118
#' License: https://github.com/rstudio/renv/blob/main/LICENSE

R/global_params.R

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,70 @@
55
#'
66
#' @param quiet A `logical` value indicating whether to suppress messages. Can be `TRUE` or `FALSE`.
77
#' @keywords internal
8-
global_quiet_param <- function(quiet){
8+
global_quiet_param <- function(quiet) {
99
# this is just a placeholder for global quiet parameter
1010
}
11+
12+
#' @title rJava Path-Locking Documentation
13+
#'
14+
#' @description
15+
#' Documentation template for rJava path-locking behavior.
16+
#'
17+
#' @section rJava Path-Locking:
18+
#' **Important for \strong{rJava} Users**: This function sets environment variables
19+
#' (JAVA_HOME, PATH) that affect both command-line Java tools and \strong{rJava} initialization.
20+
#' However, due to \strong{rJava}'s path-locking behavior when \code{\link[rJava]{.jinit}} is called
21+
#' (see \url{https://github.com/s-u/rJava/issues/25}, \url{https://github.com/s-u/rJava/issues/249}, and \url{https://github.com/s-u/rJava/issues/334}),
22+
#' this function must be called **BEFORE** \code{\link[rJava]{.jinit}} is invoked. Once \code{\link[rJava]{.jinit}}
23+
#' initializes, the Java version is locked for that R session and cannot be changed without restarting R.
24+
#'
25+
#' \code{\link[rJava]{.jinit}} is invoked (and Java locked) when you:
26+
#' \itemize{
27+
#' \item Explicitly call \code{library(rJava)}
28+
#' \item Load any package that imports \strong{rJava} (which auto-loads it as a dependency)
29+
#' \item Even just use IDE autocomplete with \code{rJava::} (this triggers initialization!)
30+
#' \item Call any \strong{rJava}-dependent function
31+
#' }
32+
#'
33+
#' Once any of these happen, the Java version used by \strong{rJava} for that session is locked in.
34+
#' For command-line Java tools that don't use \strong{rJava}, this function can be called at any
35+
#' time to switch Java versions for subsequent system calls.
36+
#'
37+
#' @keywords internal
38+
rjava_path_locking_note <- function() {
39+
# Placeholder for rJava path-locking documentation
40+
}
41+
42+
#' @title Global Use Cache Parameter
43+
#'
44+
#' @description
45+
#' Documentation for the `.use_cache` parameter, used for performance optimization.
46+
#'
47+
#' @param .use_cache A `logical` value controlling caching behavior. If `FALSE` (default),
48+
#' performs a fresh check each time (safe, reflects current state). If `TRUE`, uses
49+
#' session-scoped cached results for performance in loops or repeated calls.
50+
#'
51+
#' **Caching Behavior:**
52+
#' - Session-scoped: Cache is cleared when R restarts
53+
#' - Key-based for version checks: Changes to JAVA_HOME create new cache entries
54+
#' - System-wide for scanning: Always recalculates current default Java
55+
#'
56+
#' **Performance Benefits:**
57+
#' - First call: ~37-209ms (depending on operation)
58+
#' - Cached calls: <1ms
59+
#' - Prevents 30-100ms delays on every call in performance-critical code
60+
#'
61+
#' **When to Enable:**
62+
#' - Package initialization code (`.onLoad` or similar)
63+
#' - Loops calling the same function multiple times
64+
#' - Performance-critical paths with frequent version checks
65+
#'
66+
#' **When to Keep Default (FALSE):**
67+
#' - Interactive use (one-off checks)
68+
#' - When you need current data reflecting recent Java installations
69+
#' - General-purpose function calls that aren't time-critical
70+
#'
71+
#' @keywords internal
72+
global_use_cache_param <- function(.use_cache) {
73+
# this is just a placeholder for global .use_cache parameter
74+
}

0 commit comments

Comments
 (0)