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 416551cdc75295e253cfcb1445618d8bb80fc9dc
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Dec 5 16:49:32 2024 +0100

    Add a public `Configuration.shutdown()` method.
---
 .../factory/ConcurrentAuthorityFactory.java        |  4 +--
 .../main/org/apache/sis/setup/Configuration.java   | 28 +++++++++++++++-
 .../main/org/apache/sis/system/Shutdown.java       | 38 +++++++++++-----------
 3 files changed, 48 insertions(+), 22 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 3e675a5408..ec51343a7d 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
@@ -1669,7 +1669,7 @@ public abstract class ConcurrentAuthorityFactory<DAO 
extends GeodeticAuthorityFa
 
         /** String representation used by {@link CacheRecord}. */
         @Override public String toString() {
-            final StringBuilder buffer = new StringBuilder();
+            final var buffer = new StringBuilder();
             if (type instanceof Class<?>) {
                 buffer.append("Code[“").append(code);
                 if (buffer.length() > 15) {                     // Arbitrary 
limit in string length.
@@ -2090,7 +2090,7 @@ public abstract class ConcurrentAuthorityFactory<DAO 
extends GeodeticAuthorityFa
      */
     private static <DAO extends GeodeticAuthorityFactory> List<DAO> 
clear(final Deque<DataAccessRef<DAO>> availableDAOs) {
         assert Thread.holdsLock(availableDAOs);
-        final List<DAO> factories = new ArrayList<>(availableDAOs.size());
+        final var factories = new ArrayList<DAO>(availableDAOs.size());
         DataAccessRef<DAO> dao;
         while ((dao = availableDAOs.pollFirst()) != null) {
             factories.add(dao.factory);
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java
index e777d74735..a0f0fa7618 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java
@@ -23,6 +23,9 @@ import java.util.function.Supplier;
 import java.util.concurrent.TimeUnit;
 import javax.sql.DataSource;
 import java.sql.SQLException;
+import org.apache.sis.system.Shutdown;
+import org.apache.sis.system.SystemListener;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.privy.MetadataServices;
 
 
@@ -54,7 +57,7 @@ import org.apache.sis.util.privy.MetadataServices;
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.5
  * @since   1.0
  */
 public final class Configuration {
@@ -146,4 +149,27 @@ public final class Configuration {
     public void setDatabase(final Supplier<DataSource> source) {
         
MetadataServices.getInstance().setDataSource(Objects.requireNonNull(source));
     }
+
+    /**
+     * Shutdowns the Apache SIS library.
+     * This method close database connections and stops daemon threads.
+     * <strong>The Apache SIS library shall not be used anymore after this 
method call.</strong>
+     * Any use of Apache SIS after this method call may cause undermined 
behavior.
+     * In particular, it may cause memory leaks.
+     *
+     * <h4>When to use</h4>
+     * This method should generally <strong>not</strong> be invoked, because 
Apache SIS registers itself
+     * a {@linkplain Runtime#addShutdownHook(Thread) shutdown hook} to the 
Java Virtual Machine.
+     * This method may be useful in environments that do not allow the use of 
shutdown hooks,
+     * or when waiting for the <abbr>JVM</abbr> shutdown is overly 
conservative.
+     *
+     * @since 1.5
+     */
+    public void shutdown() {
+        try {
+            Shutdown.stop(Configuration.class);
+        } catch (Exception e) {
+            Logging.unexpectedException(SystemListener.LOGGER, 
Configuration.class, "stop", e);
+        }
+    }
 }
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 d926758b75..6015812e60 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
@@ -16,8 +16,8 @@
  */
 package org.apache.sis.system;
 
-import java.util.List;
-import java.util.ArrayList;
+import java.util.Deque;
+import java.util.ArrayDeque;
 import java.util.Objects;
 import java.util.concurrent.Callable;
 import org.apache.sis.util.logging.Logging;
@@ -44,7 +44,7 @@ public final class Shutdown extends Thread {
     /**
      * The resources to dispose. Most recently added resources are last.
      */
-    private static final List<Callable<?>> resources = new ArrayList<>();
+    private static final Deque<Callable<?>> resources = new ArrayDeque<>();
 
     /**
      * Creates the thread to be executed at shutdown time.
@@ -83,7 +83,7 @@ public final class Shutdown extends Thread {
 
     /**
      * Invoked if the Apache SIS library is executed from an environment that 
provide its own shutdown hook.
-     * Example of such environments are OSG and servlet containers. In such 
case, the shutdown hook will not
+     * Example of such environments are OSGi and servlet containers. In such 
case, the shutdown hook will not
      * be registered to the JVM {@link Runtime}.
      *
      * @param  env  a description of the container. Should contain version 
information if possible.
@@ -108,10 +108,13 @@ public final class Shutdown extends Thread {
     public static void register(final Callable<?> resource) {
         synchronized (resources) {
             assert !resources.contains(resource);
-            resources.add(resource);
-            if (hook == null && container == null) {
+            resources.addLast(resource);
+            if (hook == null && container == null) try {
                 hook = new Shutdown();
                 Runtime.getRuntime().addShutdownHook(hook);
+            } catch (SecurityException e) {
+                hook = null;
+                Logging.recoverableException(SystemListener.LOGGER, 
Shutdown.class, "register", e);
             }
         }
     }
@@ -121,26 +124,22 @@ public final class Shutdown extends Thread {
      */
     private static void removeShutdownHook() {
         assert Thread.holdsLock(resources);
-        if (hook != null) {
+        if (hook != null) try {
             Runtime.getRuntime().removeShutdownHook(hook);
             hook = null;
+        } catch (SecurityException e) {
+            Logging.recoverableException(SystemListener.LOGGER, 
Shutdown.class, "removeShutdownHook", e);
         }
     }
 
     /**
      * Unregisters a code from execution at JVM shutdown time.
-     * This method uses identity comparison (it does not use {@link 
Object#equals(Object)}).
      *
      * @param  resource  the resource disposal to cancel execution.
      */
     public static void unregister(final Callable<?> resource) {
         synchronized (resources) {
-            for (int i = resources.size(); --i>=0;) {       // Check most 
recently added resources first.
-                if (resources.get(i) == resource) {
-                    resources.remove(i);
-                    break;
-                }
-            }
+            resources.removeLastOccurrence(resource);
         }
     }
 
@@ -148,8 +147,8 @@ public final class Shutdown extends Thread {
      * Unregisters the supervisor MBean, executes the disposal tasks and 
shutdowns the {@code org.apache.sis.util} threads.
      * This method may be invoked at JVM shutdown, or if a container like OSGi 
is unloaded the SIS library.
      *
-     * @param  caller  the class invoking this method, to be used only for 
logging purpose, or {@code null}
-     *         if the logging system is not available anymore (i.e. the JVM 
itself is shutting down).
+     * @param  caller  the class invoking this method (used for logging 
purpose), or {@code null} if this method
+     *         is invoked at JVM shutdown time (in which case the logging 
system is not available anymore).
      * @throws Exception if an error occurred during unregistration of the 
supervisor MBean
      *         or during a resource disposal.
      */
@@ -157,6 +156,7 @@ public final class Shutdown extends Thread {
         synchronized (resources) {
             container = "Shutdown";
             if (caller != null) {
+                // Remove only if JVM shutdown is not already in progress.
                 removeShutdownHook();
             }
         }
@@ -176,9 +176,9 @@ public final class Shutdown extends Thread {
          * invoke Shutdown.[un]register(Disposable), but we nevertheless make 
the loop robust to this case.
          */
         synchronized (resources) {
-            int i;
-            while ((i = resources.size()) != 0) try {       // In case run() 
modifies the resources list.
-                resources.remove(i - 1).call();             // Dispose most 
recently added resources first.
+            Callable<?> r;
+            while ((r = resources.pollLast()) != null) try {
+                r.call();
             } catch (Exception e) {
                 if (exception != null) {
                     e.addSuppressed(exception);

Reply via email to