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. *