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 b1b0a7fe57f3081e4b9aaea79c1fbb2cc71d2eb3
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sat Sep 14 16:19:49 2024 +0200

    Use library-wide cleaner and shutdown hook in `GDALStore`.
---
 .../factory/ConcurrentAuthorityFactory.java        | 24 ++++++-------
 .../src/org.apache.sis.util/main/module-info.java  |  1 +
 .../main/org/apache/sis/system/Cleaners.java       | 40 ++++++++++++++++++++++
 .../apache/sis/system/ReferenceQueueConsumer.java  | 13 ++++---
 .../main/org/apache/sis/system/Shutdown.java       |  2 +-
 .../main/org/apache/sis/util/collection/Cache.java |  1 +
 .../org/apache/sis/test/LogRecordCollector.java    |  2 +-
 .../main/org/apache/sis/storage/gdal/GDAL.java     |  2 +-
 .../org/apache/sis/storage/gdal/GDALStore.java     |  5 +--
 .../apache/sis/storage/gdal/GDALStoreProvider.java | 11 ++----
 .../apache/sis/storage/panama/LibraryLoader.java   |  3 +-
 .../apache/sis/storage/panama/NativeFunctions.java | 14 +++++++-
 12 files changed, 85 insertions(+), 33 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
index 6f4fecb534..354339a2fa 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
@@ -31,7 +31,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.logging.LogRecord;
 import java.util.logging.Level;
 import java.lang.ref.WeakReference;
-import java.lang.ref.PhantomReference;
 import java.io.PrintWriter;
 import java.lang.reflect.Method;
 import javax.measure.Unit;
@@ -49,7 +48,6 @@ import org.opengis.parameter.ParameterDescriptor;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.Printable;
-import org.apache.sis.util.Disposable;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Messages;
@@ -59,7 +57,7 @@ import org.apache.sis.util.collection.Cache;
 import org.apache.sis.util.privy.CollectionsExt;
 import org.apache.sis.util.privy.Constants;
 import org.apache.sis.metadata.simple.SimpleCitation;
-import org.apache.sis.system.ReferenceQueueConsumer;
+import org.apache.sis.system.Cleaners;
 import org.apache.sis.system.DelayedExecutor;
 import org.apache.sis.system.DelayedRunnable;
 import org.apache.sis.system.Configuration;
@@ -305,6 +303,7 @@ public abstract class ConcurrentAuthorityFactory<DAO 
extends GeodeticAuthorityFa
      *        If more than this number of threads are querying this {@code 
ConcurrentAuthorityFactory} concurrently,
      *        additional threads will be blocked until a Data Access Object 
become available.
      */
+    @SuppressWarnings("this-escape")        // Phantom reference.
     protected ConcurrentAuthorityFactory(final Class<DAO> dataAccessClass,
             final int maxStrongReferences, final int maxConcurrentQueries)
     {
@@ -347,7 +346,9 @@ public abstract class ConcurrentAuthorityFactory<DAO 
extends GeodeticAuthorityFa
          *   2) Closes the Data Access Objects at JVM shutdown time if the 
application is standalone,
          *      or when the bundle is uninstalled if running inside an OSGi or 
Servlet container.
          */
-        Shutdown.register(new ShutdownHook<>(this));
+        final var closer = new ShutdownHook<>(availableDAOs);
+        Cleaners.SHARED.register(this, closer);
+        Shutdown.register(closer);
     }
 
     /**
@@ -2053,29 +2054,26 @@ public abstract class ConcurrentAuthorityFactory<DAO 
extends GeodeticAuthorityFa
      * when the Java Virtual Machine is shutdown, or when the module is 
uninstalled by the OSGi or Servlet container.
      *
      * <p><strong>Do not keep reference to the enclosing factory</strong> - in 
particular,
-     * this class must not be static - otherwise the factory would never been 
garbage collected.</p>
+     * this class must be static - otherwise the factory would never been 
garbage collected.</p>
      */
-    private static final class ShutdownHook<DAO extends 
GeodeticAuthorityFactory>           // MUST be static!
-            extends PhantomReference<ConcurrentAuthorityFactory<DAO>> 
implements Disposable, Callable<Object>
-    {
+    private static final class ShutdownHook<DAO extends 
GeodeticAuthorityFactory> implements Runnable, Callable<Object> {
         /**
          * The {@link ConcurrentAuthorityFactory#availableDAOs} queue.
          */
         private final Deque<DataAccessRef<DAO>> availableDAOs;
 
         /**
-         * Creates a new shutdown hook for the given factory.
+         * Creates a new shutdown hook.
          */
-        ShutdownHook(final ConcurrentAuthorityFactory<DAO> factory) {
-            super(factory, ReferenceQueueConsumer.QUEUE);
-            availableDAOs = factory.availableDAOs;
+        ShutdownHook(final Deque<DataAccessRef<DAO>> availableDAOs) {
+            this.availableDAOs = availableDAOs;
         }
 
         /**
          * Invoked indirectly by the garbage collector when the {@link 
ConcurrentAuthorityFactory} is disposed.
          */
         @Override
-        public void dispose() {
+        public void run() {
             Shutdown.unregister(this);
             try {
                 call();
diff --git a/endorsed/src/org.apache.sis.util/main/module-info.java 
b/endorsed/src/org.apache.sis.util/main/module-info.java
index 612114cee6..9c6a2c0b53 100644
--- a/endorsed/src/org.apache.sis.util/main/module-info.java
+++ b/endorsed/src/org.apache.sis.util/main/module-info.java
@@ -145,6 +145,7 @@ module org.apache.sis.util {
             org.apache.sis.storage,
             org.apache.sis.storage.sql,
             org.apache.sis.storage.netcdf,
+            org.apache.sis.storage.gdal,                // In the "incubator" 
sub-project.
             org.apache.sis.portrayal,
             org.apache.sis.console,
             org.apache.sis.webapp,                      // In the "incubator" 
sub-project.
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Cleaners.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Cleaners.java
new file mode 100644
index 0000000000..345c135715
--- /dev/null
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Cleaners.java
@@ -0,0 +1,40 @@
+/*
+ * 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.system;
+
+import java.lang.ref.Cleaner;
+
+
+/**
+ * The system-wide cleaner for garbage-collected objects. This class should be 
used instead of
+ * {@link ReferenceQueueConsumer} when the caller do not need to access the 
referenced object.
+ * In other words, this class should be used when a phantom reference is 
sufficient.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final class Cleaners {
+    /**
+     * The system-wide cleaner shared by all classes of the <abbr>SIS</abbr> 
library.
+     */
+    public static final Cleaner SHARED = Cleaner.create();
+
+    /**
+     * Do not allow instantiation of this class.
+     */
+    private Cleaners() {
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/ReferenceQueueConsumer.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/ReferenceQueueConsumer.java
index a9f8b4fac6..b20f34f7d2 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/ReferenceQueueConsumer.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/ReferenceQueueConsumer.java
@@ -24,10 +24,10 @@ import org.apache.sis.util.logging.Logging;
 
 /**
  * A thread processing all {@link Reference} instances enqueued in a {@link 
ReferenceQueue}.
- * This is the central place where <em>every</em> weak references produced by 
the SIS library
- * are consumed. This thread will invoke the {@link Disposable#dispose()} 
method for each
- * references enqueued by the garbage collector. Those references 
<strong>must</strong>
- * implement the {@link Disposable} interface.
+ * This is the central place where weak references produced by the SIS library 
are consumed.
+ * This thread will invoke the {@link Disposable#dispose()} method for each 
references
+ * enqueued by the garbage collector.
+ * Those references <strong>must</strong> implement the {@link Disposable} 
interface.
  *
  * Example:
  *
@@ -45,6 +45,11 @@ import org.apache.sis.util.logging.Logging;
  *     }
  *     }
  *
+ * <h2>When to use</h2>
+ * This class is useful when the caller need to keep the weak reference.
+ * If a phantom reference is sufficient, consider using {@link Cleaners} 
instead.
+ * The latter is based on standard <abbr>JDK</abbr> classes and provides more 
safety.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  */
 public final class ReferenceQueueConsumer extends DaemonThread {
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java
index ffc294281c..d926758b75 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java
@@ -24,7 +24,7 @@ import org.apache.sis.util.logging.Logging;
 
 
 /**
- * A central place where to manage SIS shutdown process.
+ * A central place where to manage SIS shutdown processes.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Guilhem Legal (Geomatys)
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java
index ca17d58b30..0df482b7d8 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java
@@ -1182,6 +1182,7 @@ public class Cache<K,V> extends AbstractMap<K,V> 
implements ConcurrentMap<K,V> {
          */
         @Override
         public void run() {
+            @SuppressWarnings("LocalVariableHidesMemberVariable")
             final V value = this.value;
             if (value != null) {
                 adjustReferences(key, value);
diff --git 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LogRecordCollector.java
 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LogRecordCollector.java
index fc80f985e2..1a00e02a39 100644
--- 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LogRecordCollector.java
+++ 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LogRecordCollector.java
@@ -226,7 +226,7 @@ final class LogRecordCollector extends Handler implements 
Runnable {
      * If some tests in the class emitted unexpected log records,
      * prints a table showing which tests caused logging.
      *
-     * <p>Note: this was use to be a JUnit {@code afterAll(ExtensionContext)} 
method.
+     * <p>Note: this was used to be a JUnit {@code afterAll(ExtensionContext)} 
method.
      * But we want this method to be executed after all tests in the project,
      * not after each class.</p>
      */
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
index 78f78d1c8c..5ad892c414 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
@@ -43,7 +43,7 @@ import org.apache.sis.util.logging.Logging;
  * @author  Quentin Bialota (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  */
-final class GDAL extends NativeFunctions implements Runnable {
+final class GDAL extends NativeFunctions {
     /**
      * The global instance, created when first needed.
      * This field shall be read and updated in a synchronized block.
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
index ad92e76d58..6182b45554 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
@@ -54,6 +54,7 @@ import org.apache.sis.storage.base.ResourceOnFileSystem;
 import org.apache.sis.storage.base.URIDataStore;
 import org.apache.sis.util.privy.UnmodifiableArrayList;
 import org.apache.sis.util.iso.DefaultNameFactory;
+import org.apache.sis.system.Cleaners;
 
 
 /**
@@ -159,7 +160,7 @@ public class GDALStore extends DataStore implements 
Aggregate, ResourceOnFileSys
         path     = connector.getStorageAs(Path.class);
         location = connector.commit(URI.class, GDALStoreProvider.NAME);
         opener   = Opener.read(provider, Opener.toURL(location, path), 
drivers);
-        closer   = GDALStoreProvider.CLEANERS.register(this, opener);   // 
Must do now in case of exception before completion.
+        closer   = Cleaners.SHARED.register(this, opener);      // Must do now 
in case of exception before completion.
         handle   = opener.handle;
     }
 
@@ -180,7 +181,7 @@ public class GDALStore extends DataStore implements 
Aggregate, ResourceOnFileSys
         location = parent.location;
         factory  = parent.factory;
         opener   = Opener.read(getProvider(), url, driver);
-        closer   = GDALStoreProvider.CLEANERS.register(this, opener);   // 
Must do now in case of exception before completion.
+        closer   = Cleaners.SHARED.register(this, opener);      // Must do now 
in case of exception before completion.
         handle   = opener.handle;
     }
 
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
index ce9e3ce455..885c44efaa 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
@@ -21,7 +21,6 @@ import java.util.Optional;
 import java.util.logging.Logger;
 import java.util.function.Function;
 import java.time.LocalDate;
-import java.lang.ref.Cleaner;
 import java.nio.file.Path;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
@@ -41,6 +40,7 @@ import org.apache.sis.io.stream.InternalOptionKey;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.setup.OptionKey;
+import org.apache.sis.system.Cleaners;
 import org.apache.sis.util.Version;
 import org.apache.sis.util.collection.TreeTable;
 
@@ -116,13 +116,6 @@ public class GDALStoreProvider extends DataStoreProvider {
      */
     private LibraryStatus status;
 
-    /**
-     * Actions to execute on garbage-collection of {@link GDALStore} or {@code 
GDALStoreProvider}.
-     *
-     * @todo Move in a package shared by the rest of Apache SIS.
-     */
-    static final Cleaner CLEANERS = Cleaner.create();
-
     /**
      * Creates a new provider which will load the <abbr>GDAL</abbr> library 
from the default library path.
      */
@@ -139,7 +132,7 @@ public class GDALStoreProvider extends DataStoreProvider {
      */
     @SuppressWarnings("this-escape")
     public GDALStoreProvider(final Path library) {
-        CLEANERS.register(this, nativeFunctions = GDAL.load(library));
+        Cleaners.SHARED.register(this, nativeFunctions = GDAL.load(library));
         status = LibraryStatus.LOADED;
     }
 
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/LibraryLoader.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/LibraryLoader.java
index f6634777d1..863d7ba48d 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/LibraryLoader.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/LibraryLoader.java
@@ -26,6 +26,7 @@ import java.util.function.Function;
 import java.lang.foreign.Arena;
 import java.lang.foreign.SymbolLookup;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.system.Shutdown;
 import org.apache.sis.util.Exceptions;
 
 
@@ -162,7 +163,7 @@ create: try {
              *
              * TODO: unregister if the library had a fatal error and should 
not be used anymore.
              */
-            Runtime.getRuntime().addShutdownHook(new Thread(instance));
+            Shutdown.register(instance);
         } catch (IllegalCallerException e) {
             error  = e;
             status = LibraryStatus.UNAUTHORIZED;
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java
index f92b831fd9..4227212271 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java
@@ -18,6 +18,7 @@ package org.apache.sis.storage.panama;
 
 import java.util.Optional;
 import java.util.OptionalInt;
+import java.util.concurrent.Callable;
 import java.lang.foreign.Arena;
 import java.lang.foreign.Linker;
 import java.lang.foreign.SymbolLookup;
@@ -35,7 +36,7 @@ import java.lang.reflect.UndeclaredThrowableException;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-public abstract class NativeFunctions implements Runnable {
+public abstract class NativeFunctions implements Runnable, Callable<Object> {
     /**
      * Name of the library which has been loaded, for information purposes.
      */
@@ -170,6 +171,17 @@ public abstract class NativeFunctions implements Runnable {
         }
     }
 
+    /**
+     * Synonymous of {@link #run()}, used in shutdown hook.
+     *
+     * @return {@code null}.
+     */
+    @Override
+    public final Object call() {
+        run();
+        return null;
+    }
+
     /**
      * Returns whether the given result is null.
      *

Reply via email to