From c29094974bf406d7ceb4f589ec7671eadc8407e0 Mon Sep 17 00:00:00 2001 From: Pierpaolo Vittorini Date: Tue, 30 Jun 2026 20:06:29 +0200 Subject: [PATCH 1/2] JRI: avoid System.exit on native load failure; fix double assign truncation The Rengine static initializer called System.exit(1) when the JRI native library failed to load. This kills the whole JVM, which is fatal when JRI is embedded in a container (application server, plugin host, ...). Leave jriLoaded == false instead and have both Rengine constructors throw an UnsatisfiedLinkError so callers can handle the failure. Also fix assign(String, REXP) for double scalars: it called Double.intValue() instead of doubleValue(), truncating the value to an integer before assigning it to R. Co-Authored-By: Claude Opus 4.8 (1M context) --- rosuda/JRI/Rengine.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/rosuda/JRI/Rengine.java b/rosuda/JRI/Rengine.java index a8461322..dcb3901b 100644 --- a/rosuda/JRI/Rengine.java +++ b/rosuda/JRI/Rengine.java @@ -24,11 +24,22 @@ public class Rengine extends Thread { if (iu == null || !iu.equals("yes")) { System.err.println("Cannot find JRI native library!\nPlease make sure that the JRI native library is in a directory listed in java.library.path.\n"); e.printStackTrace(); - System.exit(1); + // NOTE: we must NOT call System.exit() here - doing so kills the + // whole JVM, which is fatal when JRI is embedded in a container + // (application server, plugin host, ...). Instead we leave + // jriLoaded == false; the Rengine constructors throw an + // UnsatisfiedLinkError so the caller can handle the failure. + // Stand-alone apps that previously relied on the exit should + // check jriLoaded (or catch the constructor error) themselves. } } } + /** thrown by the constructors when the JRI native library could not be loaded. Keeping it as an {@link UnsatisfiedLinkError} preserves source/binary compatibility with callers that already catch that error from native calls. */ + private static UnsatisfiedLinkError nativeNotLoaded() { + return new UnsatisfiedLinkError("JRI native library is not available (System.loadLibrary(\"jri\") failed). Check java.library.path and the R/JRI installation."); + } + static Thread mainRThread = null; // constrants to be used with rniSpecialObject @@ -107,6 +118,7 @@ public static boolean inMainRThread() { */ public Rengine(String[] args, boolean runMainLoop, RMainLoopCallbacks initialCallbacks) { super(); + if (!jriLoaded) throw nativeNotLoaded(); Rsync=new Mutex(); died=false; alive=false; @@ -125,6 +137,7 @@ public Rengine(String[] args, boolean runMainLoop, RMainLoopCallbacks initialCal */ public Rengine() { super(); + if (!jriLoaded) throw nativeNotLoaded(); Rsync=new Mutex(); died=false; alive=true; @@ -697,7 +710,7 @@ public boolean assign(String sym, REXP r) { return rniAssign(sym,x1,0); } if (r.Xt == REXP.XT_DOUBLE || r.Xt == REXP.XT_ARRAY_DOUBLE) { - double[] cont = r.rtype == REXP.XT_DOUBLE?new double[]{((Double)r.cont).intValue()}:(double[])r.cont; + double[] cont = r.rtype == REXP.XT_DOUBLE?new double[]{((Double)r.cont).doubleValue()}:(double[])r.cont; long x1 = rniPutDoubleArray(cont); return rniAssign(sym,x1,0); } From 964a582c7ac080aeb9b3c4c9a489277bd882db6a Mon Sep 17 00:00:00 2001 From: Pierpaolo Vittorini Date: Tue, 30 Jun 2026 20:10:57 +0200 Subject: [PATCH 2/2] Makefile: default JDKVER to 8 (1.4 unsupported on modern JDKs) Modern JDKs (9+) reject -source/-target 1.4, so a plain 'make' failed with 'Source option 1.4 is no longer supported'. Default JDKVER to 8, the oldest level still accepted, and document that 14+ must not be used for JRI.jar because 'yield' becomes a reserved identifier there. Co-Authored-By: Claude Opus 4.8 (1M context) --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2af0613b..698ee9d3 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,13 @@ # Requires GNU make (or compatible)! JAPIURL:=http://java.sun.com/j2se/1.4.2/docs/api +# Default Java source/target level. Was 1.4, but modern JDKs (9+) no longer +# accept -source/-target 1.4, so the default is now 8 (the oldest level still +# supported). Override on the command line if needed, e.g. make JDKVER=11. +# Note: do NOT use 14 or higher when building JRI.jar - 'yield' becomes a +# reserved identifier and Rengine.java fails to compile. ifeq ($(JDKVER),) -JDKVER:=1.4 +JDKVER:=8 endif JFLAGS+=-encoding UTF-8 -target $(JDKVER) -source $(JDKVER)