This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 23f06c63c307da91a3f87fd2d40986c30d327f7b Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Tue Oct 1 13:02:30 2024 +0200 Make the GSF module independent of the GDAL module. It forces us to copy the `org.apache.sis.storage.panama` package, because we do not yet have a location for it that modules could share. However, we copy only a simplified version of that package in GSF for now. --- .../main/module-info.java | 3 - .../main/module-info.java | 3 - .../main/org/apache/sis/storage/gsf/GSF.java | 34 +++--- .../apache/sis/storage/gsf/GSFStoreProvider.java | 6 +- .../sis/storage/gsf/panama/LibraryLoader.java | 125 +++++++++++++++++++++ .../sis/storage/gsf/panama/LibraryStatus.java} | 29 +++-- .../sis/storage/gsf/panama/NativeFunctions.java | 84 ++++++++++++++ .../sis/storage/gsf/panama/package-info.java} | 20 +--- 8 files changed, 254 insertions(+), 50 deletions(-) diff --git a/incubator/src/org.apache.sis.storage.gdal/main/module-info.java b/incubator/src/org.apache.sis.storage.gdal/main/module-info.java index f191af4435..e4d87b9584 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/module-info.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/module-info.java @@ -54,9 +54,6 @@ module org.apache.sis.storage.gdal { exports org.apache.sis.storage.gdal; - exports org.apache.sis.storage.panama to - org.apache.sis.storage.gsf; // In the "incubator" sub-project. - provides org.apache.sis.storage.DataStoreProvider with org.apache.sis.storage.gdal.GDALStoreProvider; } diff --git a/incubator/src/org.apache.sis.storage.gsf/main/module-info.java b/incubator/src/org.apache.sis.storage.gsf/main/module-info.java index d006a4f811..143d3bc638 100644 --- a/incubator/src/org.apache.sis.storage.gsf/main/module-info.java +++ b/incubator/src/org.apache.sis.storage.gsf/main/module-info.java @@ -25,9 +25,6 @@ module org.apache.sis.storage.gsf { requires transitive org.apache.sis.referencing; requires transitive org.apache.sis.storage; - // For internal usage only. - requires org.apache.sis.storage.gdal; - provides org.apache.sis.storage.DataStoreProvider with org.apache.sis.storage.gsf.GSFStoreProvider; } diff --git a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java index 1a00375184..e08338175f 100644 --- a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java +++ b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java @@ -17,7 +17,6 @@ package org.apache.sis.storage.gsf; import java.lang.foreign.AddressLayout; -import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; @@ -30,9 +29,9 @@ import java.util.Optional; import java.util.logging.Level; import java.util.logging.LogRecord; import org.apache.sis.storage.DataStoreException; -import org.apache.sis.storage.panama.LibraryLoader; -import org.apache.sis.storage.panama.LibraryStatus; -import org.apache.sis.storage.panama.NativeFunctions; +import org.apache.sis.storage.gsf.panama.LibraryLoader; +import org.apache.sis.storage.gsf.panama.LibraryStatus; +import org.apache.sis.storage.gsf.panama.NativeFunctions; import org.apache.sis.util.logging.Logging; /** @@ -680,11 +679,13 @@ public class GSF extends NativeFunctions { /** * Creates the handles for all <abbr>GSF</abbr> functions which will be needed. + * This constructor is public for {@link LibraryLoader} purpose only. + * <strong>Do not use.</strong> * * @param loader the object used for loading the library. * @throws NoSuchElementException if a <abbr>GSF</abbr> function has not been found in the library. */ - private GSF(final LibraryLoader<GSF> loader) { + public GSF(final LibraryLoader loader) { super(loader); final FunctionDescriptor p = FunctionDescriptor.of(GSF.C_POINTER); @@ -729,10 +730,6 @@ public class GSF extends NativeFunctions { gsfGetPositionOffsets = lookup("gsfGetPositionOffsets", FunctionDescriptor.of(GSF.C_POINTER, Position.LAYOUT, Position.LAYOUT, GSF.C_DOUBLE, GSF.C_DOUBLE)); } - public Arena getArena() { - return arena(); - } - public int open(MemorySegment filename, int mode, MemorySegment handle) throws Throwable { return (int) gsfOpen.invokeExact(filename, mode, handle); } @@ -883,7 +880,7 @@ public class GSF extends NativeFunctions { /** * Raise an exception if code is an error code. */ - public void catchError(int error) throws GSFException { + public static void catchError(int error) throws GSFException { if (error == 0) return; String message = ERRORCODES.get(error); if (message == null) message = "UNKNOWN ERROR CODE"; @@ -899,13 +896,13 @@ public class GSF extends NativeFunctions { * In such case, the caller must be synchronized and {@link #global} must be initially null. * @return the library loader for <abbr>GSF</abbr>. */ - private static LibraryLoader<GSF> load(final boolean now) { - final var loader = new LibraryLoader<>(GSF::new); + private static LibraryLoader load(final boolean now) { + final var loader = new LibraryLoader(); if (now) { try { - global = loader.global("gsf"); + global = loader.global(); } finally { - globalStatus = loader.status(); + globalStatus = loader.status; } if (global != null) { if (GSFStoreProvider.LOGGER.isLoggable(Level.CONFIG)) { @@ -945,9 +942,11 @@ public class GSF extends NativeFunctions { */ static synchronized GSF global() throws DataStoreException { if (globalStatus == null) { - load(true).validate(GSFStoreProvider.NAME); + load(true).validate(); + } + if (globalStatus != LibraryStatus.LOADED) { + throw new DataStoreException("GSF not loaded."); } - globalStatus.report(GSFStoreProvider.NAME, null); return global; } @@ -959,7 +958,7 @@ public class GSF extends NativeFunctions { */ static synchronized Optional<GSF> tryGlobal(final String caller) { if (globalStatus == null) { - load(true).getError(GSFStoreProvider.NAME).ifPresent((record) -> log(caller, record)); + load(true).getError().ifPresent((record) -> log(caller, record)); } return Optional.ofNullable(global); } @@ -973,5 +972,4 @@ public class GSF extends NativeFunctions { private static void log(final String caller, final LogRecord record) { Logging.completeAndLog(GSFStoreProvider.LOGGER, GSFStoreProvider.class, caller, record); } - } diff --git a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSFStoreProvider.java b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSFStoreProvider.java index 4be4cdd695..f74d71e125 100644 --- a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSFStoreProvider.java +++ b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSFStoreProvider.java @@ -29,7 +29,7 @@ import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.base.StoreMetadata; import org.apache.sis.storage.base.Capability; import org.apache.sis.storage.base.URIDataStoreProvider; -import org.apache.sis.storage.panama.LibraryStatus; +import org.apache.sis.storage.gsf.panama.LibraryStatus; import org.apache.sis.parameter.ParameterBuilder; import org.apache.sis.parameter.Parameters; import org.apache.sis.system.Cleaners; @@ -114,7 +114,9 @@ public class GSFStoreProvider extends DataStoreProvider { if (status == null) { return GSF.global(); // Fetch each time (no cache) because may have changed outside this class. } - status.report(NAME, null); // Should never return if `nativeFunctions` is null. + if (status != LibraryStatus.LOADED) { + throw new DataStoreException("GSF not loaded."); + } return nativeFunctions; } diff --git a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/LibraryLoader.java b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/LibraryLoader.java new file mode 100644 index 0000000000..b42ebfeaaa --- /dev/null +++ b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/LibraryLoader.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.storage.gsf.panama; + +import java.io.File; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.lang.foreign.Arena; +import java.lang.foreign.SymbolLookup; +import org.apache.sis.storage.DataStoreException; +import org.apache.sis.storage.gsf.GSF; +import org.apache.sis.system.Shutdown; +import org.apache.sis.util.Exceptions; + + +/** + * A helper class for loading a native library. + * + * @see org.apache.sis.storage.panama.LibraryLoader + */ +public final class LibraryLoader { + /** + * Value to assign to {@link NativeFunctions#arena}. + */ + Arena arena; + + /** + * Value to assign to {@link NativeFunctions#symbols}. + */ + SymbolLookup symbols; + + /** + * Whether the library has been found. + */ + public LibraryStatus status; + + /** + * Cause of failure to load the library, or {@code null} if none. + */ + private RuntimeException error; + + /** + * Creates a new instance. + */ + public LibraryLoader() { + } + + /** + * Loads the native library from the given file. + * Callers should register the returned instance in a {@link java.lang.ref.Cleaner}. + */ + @SuppressWarnings("restricted") + public GSF load(final Path library) { + status = LibraryStatus.CANNOT_LOAD_LIBRARY; + arena = Arena.ofShared(); + final GSF instance; + try { + symbols = SymbolLookup.libraryLookup(library, arena); + status = LibraryStatus.FUNCTION_NOT_FOUND; + instance = new GSF(this); + status = LibraryStatus.LOADED; + } catch (Throwable e) { + arena.close(); + throw e; + } + return instance; + } + + /** + * Searches the native library in the default library path. + */ + @SuppressWarnings("restricted") + public GSF global() { + status = LibraryStatus.CANNOT_LOAD_LIBRARY; + String filename = (File.separatorChar == '\\') ? "gsf.dll" : "libgsf.so"; + try { + symbols = SymbolLookup.libraryLookup(filename, Arena.global()); + GSF instance = new GSF(this); + status = LibraryStatus.LOADED; + Shutdown.register(instance); + return instance; + } catch (RuntimeException e) { + error = e; + return null; + } + } + + /** + * Throws an exception if the loading of the native library failed. + */ + public void validate() throws DataStoreException { + if (error != null) { + throw new DataStoreException(error); + } + } + + /** + * Returns the error as a log message. + */ + public Optional<LogRecord> getError() { + if (error == null) { + return Optional.empty(); + } + String msg = "Cannot initialize the GSF library."; + var record = new LogRecord(Level.CONFIG, Exceptions.formatChainedMessages(null, msg, error)); + record.setThrown(error); + return Optional.of(record); + } +} diff --git a/incubator/src/org.apache.sis.storage.gsf/main/module-info.java b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/LibraryStatus.java similarity index 56% copy from incubator/src/org.apache.sis.storage.gsf/main/module-info.java copy to incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/LibraryStatus.java index d006a4f811..9b21d9afeb 100644 --- a/incubator/src/org.apache.sis.storage.gsf/main/module-info.java +++ b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/LibraryStatus.java @@ -14,20 +14,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.sis.storage.gsf.panama; + /** - * LibGSF Panama binding. + * Status of the native library. * - * @author Johann Sorel (Geomatys) + * @see org.apache.sis.storage.panama.LibraryStatus */ -module org.apache.sis.storage.gsf { - // Dependencies used in public API. - requires transitive org.apache.sis.referencing; - requires transitive org.apache.sis.storage; +public enum LibraryStatus { + /** + * The native library is ready for use. + */ + LOADED, - // For internal usage only. - requires org.apache.sis.storage.gdal; + /** + * The native library has not been found or cannot be loaded. Note: this is a merge of + * {@link org.apache.sis.storage.panama.LibraryStatus#LIBRARY_NOT_FOUND} and + * {@link org.apache.sis.storage.panama.LibraryStatus#UNAUTHORIZED}. + */ + CANNOT_LOAD_LIBRARY, - provides org.apache.sis.storage.DataStoreProvider - with org.apache.sis.storage.gsf.GSFStoreProvider; + /** + * The native library was found, but not symbol that we searched. + */ + FUNCTION_NOT_FOUND } diff --git a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/NativeFunctions.java b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/NativeFunctions.java new file mode 100644 index 0000000000..41e107b886 --- /dev/null +++ b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/NativeFunctions.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.storage.gsf.panama; + +import java.util.concurrent.Callable; +import java.lang.foreign.Arena; +import java.lang.foreign.Linker; +import java.lang.foreign.SymbolLookup; +import java.lang.foreign.FunctionDescriptor; +import java.lang.invoke.MethodHandle; + + +/** + * Base class for sets of method handles to native functions. + * + * @see org.apache.sis.storage.panama.NativeFunctions + */ +public abstract class NativeFunctions implements Runnable, Callable<Object> { + /** + * The arena used for loading the library, or {@code null} for the global arena. + */ + private final Arena arena; + + /** + * The lookup for retrieving the address of a symbol in the native library. + */ + public final SymbolLookup symbols; + + /** + * The linker to use for fetching method handles from the {@linkplain #symbols}. + */ + public final Linker linker; + + /** + * Creates a new set of handles to native functions. + */ + protected NativeFunctions(final LibraryLoader loader) { + arena = loader.arena; + symbols = loader.symbols; + linker = Linker.nativeLinker(); + } + + /** + * Returns the method handler for the <abbr>GSF</abbr> function of given name and signature. + */ + @SuppressWarnings("restricted") + protected final MethodHandle lookup(final String function, final FunctionDescriptor signature) { + return symbols.find(function).map((method) -> linker.downcallHandle(method, signature)).orElseThrow(); + } + + /** + * Unloads the native library. If the arena is global, + * then this method should not be invoked before <abbr>JVM</abbr> shutdown. + */ + @Override + public void run() { + if (arena != null) { + arena.close(); + } + } + + /** + * Synonymous of {@link #run()}, used in shutdown hook. + */ + @Override + public final Object call() { + run(); + return null; + } +} diff --git a/incubator/src/org.apache.sis.storage.gsf/main/module-info.java b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/package-info.java similarity index 61% copy from incubator/src/org.apache.sis.storage.gsf/main/module-info.java copy to incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/package-info.java index d006a4f811..ce1282d7de 100644 --- a/incubator/src/org.apache.sis.storage.gsf/main/module-info.java +++ b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/panama/package-info.java @@ -16,18 +16,10 @@ */ /** - * LibGSF Panama binding. - * - * @author Johann Sorel (Geomatys) + * Partial (simplified) copy of the {@code org.apache.sis.storage.panama} package. + * This copy is for keeping this <abbr>GSF</abbr> module independent from the <abbr>GDAL</abbr> module, + * because we do not yet have a location where to put the {@code org.apache.sis.storage.panama} package + * in a way where it can be reused by other modules. This package will be deleted in a future version + * if we find a home for the {@code org.apache.sis.storage.panama} package. */ -module org.apache.sis.storage.gsf { - // Dependencies used in public API. - requires transitive org.apache.sis.referencing; - requires transitive org.apache.sis.storage; - - // For internal usage only. - requires org.apache.sis.storage.gdal; - - provides org.apache.sis.storage.DataStoreProvider - with org.apache.sis.storage.gsf.GSFStoreProvider; -} +package org.apache.sis.storage.gsf.panama;