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); }