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 afb365202ed36e38d64f3eddb2ef4c0f056152a0
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Dec 5 12:57:02 2024 +0100

    Make the code more SecurityException-proof.
    This commit does not introduce new dependency since the Apache SIS code 
already had some `catch (SecurityException)` statements.
    While the security manager is removed in Java 24, SIS can run on Java 11 
and some softwares are still running in security-constrained environments.
    This commit will be reverted when Apache SIS will require Java 24, or when 
the `SecurityException` class will be deprecated.
---
 .../apache/sis/metadata/sql/privy/Initializer.java |  7 +++++-
 .../sis/metadata/sql/privy/LocalDataSource.java    |  9 ++++++--
 .../main/org/apache/sis/xml/MarshallerPool.java    | 12 +++++++++-
 .../org/apache/sis/xml/bind/TypeRegistration.java  |  8 ++++++-
 .../apache/sis/referencing/AuthorityFactories.java |  9 +++++++-
 .../transform/DefaultMathTransformFactory.java     | 12 +++++++++-
 .../apache/sis/storage/netcdf/base/Convention.java | 13 +++++++++--
 .../org/apache/sis/storage/DataStoreRegistry.java  | 10 +++++++-
 .../apache/sis/storage/event/StoreListeners.java   |  5 +++-
 .../org/apache/sis/converter/SystemRegistry.java   | 10 +++++++-
 .../main/org/apache/sis/pending/jdk/JDK24.txt      |  1 +
 .../apache/sis/setup/InstallationResources.java    |  7 +++++-
 .../main/org/apache/sis/system/Reflect.java        | 27 ++++++++++++++++++++++
 .../main/org/apache/sis/util/logging/Logging.java  |  5 +++-
 14 files changed, 121 insertions(+), 14 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Initializer.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Initializer.java
index 330ae42857..ee6c471520 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Initializer.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Initializer.java
@@ -111,7 +111,12 @@ public abstract class Initializer {
      * {@return initializers found on the module path}.
      */
     public static ServiceLoader<Initializer> load() {
-        return ServiceLoader.load(Initializer.class, 
Reflect.getContextClassLoader());
+        try {
+            return ServiceLoader.load(Initializer.class, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(Initializer.class, "load", e);
+            return ServiceLoader.load(Initializer.class);
+        }
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/LocalDataSource.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/LocalDataSource.java
index 5e18c53a63..d2c41b4364 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/LocalDataSource.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/LocalDataSource.java
@@ -201,8 +201,13 @@ public final class LocalDataSource implements DataSource, 
Comparable<LocalDataSo
             case HSQL:  classname = "org.hsqldb.jdbc.JDBCDataSource"; break;
             default:    throw new IllegalArgumentException(dialect.toString());
         }
-        final ClassLoader loader = Reflect.getContextClassLoader();
-        final Class<?> c = Class.forName(classname, true, loader);
+        Class<?> c;
+        try {
+            c = Class.forName(classname, true, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(Initializer.class, "getDataSource", e);     // Public 
caller of this method.
+            c = Class.forName(classname);
+        }
         source = (DataSource) c.getConstructor().newInstance();
         final Class<?>[] args = {String.class};
         switch (dialect) {
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/MarshallerPool.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/MarshallerPool.java
index e5647df787..ad162dd93c 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/MarshallerPool.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/MarshallerPool.java
@@ -180,7 +180,7 @@ public class MarshallerPool {
     @SuppressWarnings("this-escape")
     public MarshallerPool(final JAXBContext context, final Map<String,?> 
properties) throws JAXBException {
         this.context       = Objects.requireNonNull(context);
-        replacements       = ServiceLoader.load(AdapterReplacement.class, 
Reflect.getContextClassLoader());
+        replacements       = loader();
         implementation     = Implementation.detect(context);
         template           = new PooledTemplate(this, properties, 
implementation);
         marshallers        = new ConcurrentLinkedDeque<>();
@@ -188,6 +188,16 @@ public class MarshallerPool {
         isRemovalScheduled = new AtomicBoolean();
     }
 
+    /** Temporary method to be removed after Apache SIS requires Java 24. */
+    private static ServiceLoader<AdapterReplacement> loader() {
+        try {
+            return ServiceLoader.load(AdapterReplacement.class, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(AdapterReplacement.class, "<init>", e);
+            return ServiceLoader.load(AdapterReplacement.class);
+        }
+    }
+
     /**
      * Marks the given marshaller or unmarshaller available for further reuse.
      * This method:
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/TypeRegistration.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/TypeRegistration.java
index 07c45374d5..57216c6496 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/TypeRegistration.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/TypeRegistration.java
@@ -171,7 +171,13 @@ public abstract class TypeRegistration {
     private static ServiceLoader<TypeRegistration> services() {
         ServiceLoader<TypeRegistration> s = services;
         if (s == null) {
-            services = s = ServiceLoader.load(TypeRegistration.class, 
Reflect.getContextClassLoader());
+            try {
+                s = ServiceLoader.load(TypeRegistration.class, 
Reflect.getContextClassLoader());
+            } catch (SecurityException e) {
+                Reflect.log(TypeRegistration.class, "services", e);
+                s = ServiceLoader.load(TypeRegistration.class);
+            }
+            services = s;
             DelayedExecutor.schedule(new DelayedRunnable(1, TimeUnit.MINUTES) {
                 @Override public void run() {services = null;}
             });
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
index 7ecce9566b..6cebdd68b9 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
@@ -114,7 +114,14 @@ final class AuthorityFactories<T extends AuthorityFactory> 
extends LazySet<T> {
      */
     @Override
     protected Iterator<? extends T> createSourceIterator() {
-        return ServiceLoader.load(service, 
Reflect.getContextClassLoader()).iterator();
+        ServiceLoader<T> loader;
+        try {
+            loader = ServiceLoader.load(service, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(AuthorityFactories.class, "createSourceIterator", e);
+            loader = ServiceLoader.load(service);
+        }
+        return loader.iterator();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
index 069ee0f462..26aae9ac7f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
@@ -240,7 +240,17 @@ public class DefaultMathTransformFactory extends 
AbstractFactory implements Math
      * @see #reload()
      */
     public DefaultMathTransformFactory() {
-        this(ServiceLoader.load(OperationMethod.class, 
Reflect.getContextClassLoader()));
+        this(operations());
+    }
+
+    /** Temporary method to be removed after upgrade to JDK24. */
+    private static ServiceLoader<OperationMethod> operations() {
+        try {
+            return ServiceLoader.load(OperationMethod.class, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(DefaultMathTransformFactory.class, "<init>", e);
+            return ServiceLoader.load(OperationMethod.class);
+        }
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Convention.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Convention.java
index 1e5449b5c3..e5f6a72d88 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Convention.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Convention.java
@@ -72,8 +72,17 @@ public class Convention {
     /**
      * All conventions found on the module path.
      */
-    private static final ServiceLoader<Convention> AVAILABLES =
-            ServiceLoader.load(Convention.class, 
Reflect.getContextClassLoader());
+    private static final ServiceLoader<Convention> AVAILABLES;
+    static {
+        ServiceLoader<Convention> loader;
+        try {
+            loader = ServiceLoader.load(Convention.class, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(Convention.class, "<clinit>", e);
+            loader = ServiceLoader.load(Convention.class);
+        }
+        AVAILABLES = loader;
+    }
 
     /**
      * The convention to use when no specific conventions were found.
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreRegistry.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreRegistry.java
index f0ea34102f..6d1ce5d717 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreRegistry.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreRegistry.java
@@ -73,7 +73,15 @@ final class DataStoreRegistry extends 
LazySet<DataStoreProvider> {
      * provided that it can access at least the Apache SIS stores.
      */
     public DataStoreRegistry() {
-        this.loader = ServiceLoader.load(DataStoreProvider.class, 
Reflect.getContextClassLoader());
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        ServiceLoader<DataStoreProvider> loader;
+        try {
+            loader = ServiceLoader.load(DataStoreProvider.class, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(DataStoreRegistry.class, "<init>", e);
+            loader = ServiceLoader.load(DataStoreProvider.class);
+        }
+        this.loader = loader;
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
index 01d7a950c3..4a147776fe 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/StoreListeners.java
@@ -490,9 +490,12 @@ public class StoreListeners implements Localized {
             }
         }
         final var record = new LogRecord(level, message);
-        if (exception == null) {
+        if (exception == null) try {
             
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((stream)
 -> stream.filter(
                    (frame) -> setPublicSource(record, 
frame.getDeclaringClass(), frame.getMethodName())).findFirst());
+         } catch (SecurityException e) {
+            // Temporary catch to be removed after Apache SIS requires Java 24.
+            Logging.ignorableException(StoreUtilities.LOGGER, 
StoreListeners.class, "warning", e);
          } else try {
             record.setThrown(exception);
             for (final StackTraceElement e : exception.getStackTrace()) {
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/SystemRegistry.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/SystemRegistry.java
index 277376d995..8e6ada3b27 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/SystemRegistry.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/SystemRegistry.java
@@ -104,7 +104,15 @@ public final class SystemRegistry extends 
ConverterRegistry {
      */
     @Override
     protected void initialize() {
-        for (ObjectConverter<?,?> converter : 
ServiceLoader.load(ObjectConverter.class, Reflect.getContextClassLoader())) {
+        @SuppressWarnings("rawtypes")
+        ServiceLoader<ObjectConverter> loader;
+        try {
+            loader = ServiceLoader.load(ObjectConverter.class, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(SystemRegistry.class, "initialize", e);
+            loader = ServiceLoader.load(ObjectConverter.class);
+        }
+        for (ObjectConverter<?,?> converter : loader) {
             if (converter instanceof SystemConverter<?,?>) {
                 converter = ((SystemConverter<?,?>) converter).unique();
             }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK24.txt 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK24.txt
new file mode 100644
index 0000000000..d112ddb160
--- /dev/null
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK24.txt
@@ -0,0 +1 @@
+Reminder: Remove all usages of `SecurityException` when Apache SIS will 
require Java 24 or later.
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/InstallationResources.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/InstallationResources.java
index 790c6922ac..e758886961 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/InstallationResources.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/InstallationResources.java
@@ -86,7 +86,12 @@ public abstract class InstallationResources {
      * @since 1.4
      */
     public static ServiceLoader<InstallationResources> load() {
-        return ServiceLoader.load(InstallationResources.class, 
Reflect.getContextClassLoader());
+        try {
+            return ServiceLoader.load(InstallationResources.class, 
Reflect.getContextClassLoader());
+        } catch (SecurityException e) {
+            Reflect.log(InstallationResources.class, "load", e);
+            return ServiceLoader.load(InstallationResources.class);
+        }
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Reflect.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Reflect.java
index 13ecc4f589..a871ad9f58 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Reflect.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Reflect.java
@@ -20,6 +20,9 @@ import java.util.Set;
 import java.util.HashSet;
 import java.util.ServiceLoader;
 import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import org.apache.sis.util.logging.Logging;
 
 
 /**
@@ -101,4 +104,28 @@ public final class Reflect implements 
Consumer<StackWalker.StackFrame> {
             }
         }
     }
+
+    /**
+     * Whether the security exception warning has already been reported.
+     * Used for avoiding to pollute the logs with the same message repeated 
many times.
+     */
+    private static volatile boolean warningAlreadyReported;
+
+    /**
+     * Logs a warning saying that the context class loader cannot be obtained 
because of security constraints.
+     * While the security manager is deprecated and removed in Java 24, it is 
still used by a few applications
+     * on older releases.
+     *
+     * @param  caller  the class to report as the log source.
+     * @param  method  the method to report as the log source.
+     * @param  e       the exception that occurred.
+     */
+    public static void log(final Class<?> caller, final String method, final 
SecurityException e) {
+        boolean r = warningAlreadyReported;
+        warningAlreadyReported = true;
+        var record = new LogRecord(r ? Level.FINER : Level.CONFIG,
+                "Cannot get the context class loader. The Apache SIS services 
may be incomplete.");
+        record.setThrown(e);
+        Logging.completeAndLog(SystemListener.LOGGER, caller, method, record);
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Logging.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Logging.java
index 3387642587..a69a986a26 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Logging.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Logging.java
@@ -27,6 +27,7 @@ import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Classes;
 import org.apache.sis.system.Modules;
 import org.apache.sis.system.Configuration;
+import org.apache.sis.system.SystemListener;
 
 
 /**
@@ -118,7 +119,7 @@ public final class Logging extends Static {
         if (classe != null && method != null) {
             record.setSourceClassName(classe.getCanonicalName());
             record.setSourceMethodName(method);
-        } else {
+        } else try {
             /*
              * If the given class or method is null, infer them from stack 
trace. We do not document this feature
              * in public API because the rules applied here are heuristic and 
may change in any future SIS version.
@@ -128,6 +129,8 @@ public final class Logging extends Static {
                     inferCaller(fl, (classe != null) ? 
classe.getCanonicalName() : null, method,
                             stream.filter((frame) -> 
Classes.isPublic(frame.getDeclaringClass()))
                                   
.map((StackWalker.StackFrame::toStackTraceElement)), record));
+        } catch (SecurityException e) {
+            ignorableException(SystemListener.LOGGER, Logging.class, 
"completeAndLog", e);
         }
         logger.log(record);
     }

Reply via email to