From e3f6ac9e5e95e40bd9b98428772bdbddd2f64609 Mon Sep 17 00:00:00 2001 From: Sean Arms <67096+lesserwhirls@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:46:49 -0600 Subject: [PATCH 1/3] Fix netCDF-C loading in RuntimeConfigParser Use the correct method name when loading the library by name and path. Fixes Unidata/netcdf-java#1473 --- cdm/core/src/main/java/ucar/nc2/NetcdfFileWriter.java | 1 - .../main/java/ucar/nc2/util/xml/RuntimeConfigParser.java | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cdm/core/src/main/java/ucar/nc2/NetcdfFileWriter.java b/cdm/core/src/main/java/ucar/nc2/NetcdfFileWriter.java index 19647bc69b..f6bae2a274 100644 --- a/cdm/core/src/main/java/ucar/nc2/NetcdfFileWriter.java +++ b/cdm/core/src/main/java/ucar/nc2/NetcdfFileWriter.java @@ -194,7 +194,6 @@ protected NetcdfFileWriter(Version version, String location, boolean isExisting, if (version.useJniIosp()) { IOServiceProviderWriter spi; try { - // Nc4Iosp.setLibraryAndPath(path, name); Class iospClass = this.getClass().getClassLoader().loadClass("ucar.nc2.jni.netcdf.Nc4Iosp"); Constructor ctor = iospClass.getConstructor(Version.class); spi = ctor.newInstance(version); diff --git a/cdm/core/src/main/java/ucar/nc2/util/xml/RuntimeConfigParser.java b/cdm/core/src/main/java/ucar/nc2/util/xml/RuntimeConfigParser.java index 290544b026..93bd444698 100644 --- a/cdm/core/src/main/java/ucar/nc2/util/xml/RuntimeConfigParser.java +++ b/cdm/core/src/main/java/ucar/nc2/util/xml/RuntimeConfigParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2025 John Caron and University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ @@ -269,7 +269,7 @@ public static void read(org.jdom2.Element root, StringBuilder errlog) { // so we cannot refer to the Nc4Iosp.class and NetcdfClibrary.class object. String nc4IospClassName = "ucar.nc2.jni.netcdf.Nc4Iosp"; // The setLibraryAndPath method from Nc4Iosp has been deprecated - // and splited into separated class: NetcdfClibrary + // and split into separated class: NetcdfClibrary String netcdfClibraryClassName = "ucar.nc2.ffi.netcdf.NetcdfClibrary"; /* * @@ -285,8 +285,9 @@ public static void read(org.jdom2.Element root, StringBuilder errlog) { if (path != null && name != null) { // reflection is used to decouple optional jars try { - Class netcdfClibraryClass = RuntimeConfigParser.class.getClassLoader().loadClass(netcdfClibraryClassName); - Method method = netcdfClibraryClass.getMethod("setLibraryAndPath", String.class, String.class); + Class netcdfClibraryClass = + RuntimeConfigParser.class.getClassLoader().loadClass(netcdfClibraryClassName); + Method method = netcdfClibraryClass.getMethod("setLibraryNameAndPath", String.class, String.class); method.invoke(null, path, name); // static method has null for object } catch (Throwable e) { errlog.append(netcdfClibraryClassName + " is not on classpath\n"); From 96c7af9855e8c2e99a68f3f4b09ace724ce2f467 Mon Sep 17 00:00:00 2001 From: Sean Arms <67096+lesserwhirls@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:13:35 -0600 Subject: [PATCH 2/3] Add test to detect failing reflection for netcdf-c runtime loading --- netcdf4/build.gradle | 1 + .../nc2/util/xml/TestRuntimeConfigParser.java | 34 +++++++++++++++++++ .../resources/runtimeConfig/nj22Config.xml | 7 ++++ 3 files changed, 42 insertions(+) create mode 100644 netcdf4/src/test/java/ucar/nc2/util/xml/TestRuntimeConfigParser.java create mode 100644 netcdf4/src/test/resources/runtimeConfig/nj22Config.xml diff --git a/netcdf4/build.gradle b/netcdf4/build.gradle index 5f31ff8062..520d6cc5aa 100644 --- a/netcdf4/build.gradle +++ b/netcdf4/build.gradle @@ -19,6 +19,7 @@ dependencies { testImplementation project(':cdm-test-utils') testImplementation 'com.google.truth:truth' + testImplementation 'org.jdom:jdom2' testRuntimeOnly 'ch.qos.logback:logback-classic' } diff --git a/netcdf4/src/test/java/ucar/nc2/util/xml/TestRuntimeConfigParser.java b/netcdf4/src/test/java/ucar/nc2/util/xml/TestRuntimeConfigParser.java new file mode 100644 index 0000000000..a9eb7b8502 --- /dev/null +++ b/netcdf4/src/test/java/ucar/nc2/util/xml/TestRuntimeConfigParser.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE.txt for license information. + */ + +package ucar.nc2.util.xml; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Paths; +import org.junit.Test; + +public class TestRuntimeConfigParser { + @Test + public void testReflection() throws IOException { + // not testing that the loading actually work, but testing that the reflection + // calls in the RuntimeConfigParser work + File nj22config = + Paths.get("src/test/resources/runtimeConfig/nj22Config.xml").toAbsolutePath().normalize().toFile(); + StringBuilder errlog = new StringBuilder(); + try (FileInputStream fis = new FileInputStream(nj22config)) { + RuntimeConfigParser.read(fis, errlog); + } + String err = errlog.toString(); + assertThat(err).isNotEmpty(); + // since this test lives in the netcdf4 subproject, we should only see + // the "is not on classpath" message if the reflection call used in the + // RuntimeConfigParser code fails + assertThat(err).doesNotContain("is not on classpath"); + } +} diff --git a/netcdf4/src/test/resources/runtimeConfig/nj22Config.xml b/netcdf4/src/test/resources/runtimeConfig/nj22Config.xml new file mode 100644 index 0000000000..da9c625a72 --- /dev/null +++ b/netcdf4/src/test/resources/runtimeConfig/nj22Config.xml @@ -0,0 +1,7 @@ + + + /not/a/real/path + netcdf + false + + \ No newline at end of file From c5f9486269509f5cd855b249b2368525e12282fc Mon Sep 17 00:00:00 2001 From: Sean Arms <67096+lesserwhirls@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:14:17 -0600 Subject: [PATCH 3/3] Update netcdf-c loading docs --- docs/src/site/pages/netcdfJava/netcdf4Clibrary.md | 3 ++- .../site/pages/netcdfJava_tutorial/runtime/runtimeloading.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/src/site/pages/netcdfJava/netcdf4Clibrary.md b/docs/src/site/pages/netcdfJava/netcdf4Clibrary.md index 0f5738d718..d64d3c68b0 100644 --- a/docs/src/site/pages/netcdfJava/netcdf4Clibrary.md +++ b/docs/src/site/pages/netcdfJava/netcdf4Clibrary.md @@ -101,7 +101,8 @@ For standalone CDM library use, you can: * create a system environment variable: `JNA_PATH=/path/to/library` * set a Java property on the command line: `-Djna.library.path=/path/to/library` * set the library path and name in the runtime configuration file -* directly call `Nc4Iosp.setLibraryAndPath()` from your Java program +* directly call `ucar.nc2.ffi.netcdf.NetcdfClibrary.setLibraryNameAndPath` from your Java program + (Note: this must be called prior to calling `isLibraryPresent()` or `getForeignFunctionInterface()` as the C library can only be successfully loaded once) In all cases, we recommended that you use an absolute path to specify the library location. diff --git a/docs/src/site/pages/netcdfJava_tutorial/runtime/runtimeloading.md b/docs/src/site/pages/netcdfJava_tutorial/runtime/runtimeloading.md index a0aa52623f..98374f269a 100644 --- a/docs/src/site/pages/netcdfJava_tutorial/runtime/runtimeloading.md +++ b/docs/src/site/pages/netcdfJava_tutorial/runtime/runtimeloading.md @@ -1,6 +1,6 @@ --- title: Runtime loading -last_updated: 2021-06-08 +last_updated: 2025-08-12 sidebar: netcdfJavaTutorial_sidebar permalink: runtime_loading.html toc: false