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 4984b31e9c617a63b3a5bdf9a31f539064dea6c3 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sat Jun 10 12:42:31 2023 +0200 Replace reflection by `ServiceLoader` uses in the `OptionalDependency` class. This is needed because reflection no longer works after module encapsulation. --- .../sis/internal/metadata/ReferencingServices.java | 17 ++++---- .../org.apache.sis.internal.util.MetadataServices | 1 + ...pache.sis.internal.metadata.ReferencingServices | 1 + .../sis/internal/system/OptionalDependency.java | 50 ++++++---------------- .../apache/sis/internal/util/MetadataServices.java | 12 +++--- 5 files changed, 30 insertions(+), 51 deletions(-) diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java index 8e297de80f..7ab7d3e8a7 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java @@ -18,6 +18,7 @@ package org.apache.sis.internal.metadata; import java.util.Locale; import java.util.TimeZone; +import java.util.ServiceLoader; import java.text.Format; import org.opengis.geometry.Envelope; import org.opengis.geometry.DirectPosition; @@ -37,11 +38,8 @@ import org.apache.sis.internal.system.Modules; /** * Provides access to services defined in the {@code "sis-referencing"} module. - * This class searches for the {@link org.apache.sis.internal.referencing.ServicesForMetadata} - * implementation using Java reflection. - * - * <p>This class also opportunistically defines some methods and constants related to - * <cite>"referencing by coordinates"</cite> but needed by metadata.</p> + * This class also opportunistically defines some methods and constants related + * to <cite>"referencing by coordinates"</cite> but needed by metadata. * * @author Martin Desruisseaux (Geomatys) * @version 1.4 @@ -62,6 +60,9 @@ public class ReferencingServices extends OptionalDependency { /** * The services, fetched when first needed. + * Guaranteed non-null after the search was done, even if the service implementation was not found. + * + * @see #getInstance() */ private static volatile ReferencingServices instance; @@ -82,7 +83,6 @@ public class ReferencingServices extends OptionalDependency { @Override protected final void classpathChanged() { synchronized (ReferencingServices.class) { - super.classpathChanged(); instance = null; } } @@ -100,12 +100,11 @@ public class ReferencingServices extends OptionalDependency { c = instance; if (c == null) { /* - * Double-checked locking: okay since Java 5 provided that the 'instance' field is volatile. + * Double-checked locking: okay since Java 5 provided that the `instance` field is volatile. * In the particular case of this class, the intent is to ensure that SystemListener.add(…) * is invoked only once. */ - c = getInstance(ReferencingServices.class, Modules.METADATA, "sis-referencing", - "org.apache.sis.internal.referencing.ServicesForMetadata"); + c = getInstance(ReferencingServices.class, ServiceLoader.load(ReferencingServices.class), "sis-referencing"); if (c == null) { c = new ReferencingServices(); } diff --git a/core/sis-metadata/src/main/resources/META-INF/services/org.apache.sis.internal.util.MetadataServices b/core/sis-metadata/src/main/resources/META-INF/services/org.apache.sis.internal.util.MetadataServices new file mode 100644 index 0000000000..ebf1b0244f --- /dev/null +++ b/core/sis-metadata/src/main/resources/META-INF/services/org.apache.sis.internal.util.MetadataServices @@ -0,0 +1 @@ +org.apache.sis.internal.metadata.ServicesForUtility diff --git a/core/sis-referencing/src/main/resources/META-INF/services/org.apache.sis.internal.metadata.ReferencingServices b/core/sis-referencing/src/main/resources/META-INF/services/org.apache.sis.internal.metadata.ReferencingServices new file mode 100644 index 0000000000..8398b4e88f --- /dev/null +++ b/core/sis-referencing/src/main/resources/META-INF/services/org.apache.sis.internal.metadata.ReferencingServices @@ -0,0 +1 @@ +org.apache.sis.internal.referencing.ServicesForMetadata diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java b/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java index bc7c9964ba..f60d74d809 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/OptionalDependency.java @@ -16,6 +16,7 @@ */ package org.apache.sis.internal.system; +import java.util.ServiceLoader; import java.util.logging.Level; import java.util.logging.LogRecord; import org.apache.sis.util.logging.Logging; @@ -29,7 +30,7 @@ import org.apache.sis.util.resources.Messages; * services of the {@code "sis-referencing"} module if the latter is present on the classpath. * * @author Martin Desruisseaux (Geomatys) - * @version 0.6 + * @version 1.4 * @since 0.6 */ public abstract class OptionalDependency extends SystemListener { @@ -52,51 +53,28 @@ public abstract class OptionalDependency extends SystemListener { SystemListener.add(this); } - /** - * Invoked when the classpath is likely to have changed. - * Subclasses must override like below: - * - * {@snippet lang="java" : - * @Override - * protected final void classpathChanged() { - * synchronized (MyServices.class) { - * super.classpathChanged(); - * instance = null; - * } - * } - * } - */ - @Override - protected void classpathChanged() { - SystemListener.remove(this); - } - /** * Returns the optional dependency, or {@code null} if not found. * This is a helper method for implementation of {@code getInstance()} static method in subclasses. + * The service loader needs to be created by the caller because of Java module encapsulation rules. * - * @param <T> compile-time type of the {@code type} argument. - * @param type the subclass type. - * @param module same argument value than the one given to the {@linkplain #OptionalDependency constructor}. - * @param dependency same argument value than the one given to the {@linkplain #OptionalDependency constructor}. - * @param implementation the fully-qualified name of the class to instantiate by reflection. + * @param <T> compile-time type of the {@code type} argument. + * @param type the service type, used only if a warning needs to be logged. + * @param loader the service loader created in the module that needs the service. + * @param dependency same argument value than the one given to the {@linkplain #OptionalDependency constructor}. * @return an instance of the {@code implementation} class, or {@code null} if not found. */ protected static <T extends OptionalDependency> T getInstance(final Class<T> type, - final String module, final String dependency, final String implementation) + final ServiceLoader<T> loader, final String dependency) { - try { - return type.cast(Class.forName(implementation).newInstance()); - } catch (ClassNotFoundException exception) { - final LogRecord record = Messages.getResources(null).getLogRecord(Level.CONFIG, - Messages.Keys.OptionalModuleNotFound_1, dependency); - record.setLoggerName(module); + final T first = loader.findFirst().orElse(null); + if (first == null) { + LogRecord record = Messages.getResources(null).getLogRecord(Level.CONFIG, + Messages.Keys.OptionalModuleNotFound_1, dependency); + record.setLoggerName(type.getModule().getName()); Logging.completeAndLog(null, type, "getInstance", record); - return null; - } catch (ReflectiveOperationException exception) { - // Should never happen if we didn't broke our helper class. - throw new AssertionError(exception); } + return first; } /** diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java b/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java index ff6a7f1f12..bbc43df887 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java @@ -19,6 +19,7 @@ package org.apache.sis.internal.util; import java.text.Format; import java.util.Locale; import java.util.TimeZone; +import java.util.ServiceLoader; import java.util.MissingResourceException; import java.util.function.Supplier; import javax.sql.DataSource; @@ -36,8 +37,6 @@ import org.opengis.util.ControlledVocabulary; /** * Provides access to services defined in the {@code "sis-metadata"} module. - * This class searches for the {@link org.apache.sis.internal.metadata.ServicesForUtility} - * implementation using Java reflection. * * @author Martin Desruisseaux (Geomatys) * @version 1.4 @@ -52,6 +51,9 @@ public class MetadataServices extends OptionalDependency { /** * The services, fetched when first needed. + * Guaranteed non-null after the search was done, even if the service implementation was not found. + * + * @see #getInstance() */ private static volatile MetadataServices instance; @@ -71,7 +73,6 @@ public class MetadataServices extends OptionalDependency { @Override protected final void classpathChanged() { synchronized (MetadataServices.class) { - super.classpathChanged(); instance = null; } } @@ -89,12 +90,11 @@ public class MetadataServices extends OptionalDependency { c = instance; if (c == null) { /* - * Double-checked locking: okay since Java 5 provided that the 'instance' field is volatile. + * Double-checked locking: okay since Java 5 provided that the `instance` field is volatile. * In the particular case of this class, the intent is to ensure that SystemListener.add(…) * is invoked only once. */ - c = getInstance(MetadataServices.class, Modules.UTILITIES, "sis-metadata", - "org.apache.sis.internal.metadata.ServicesForUtility"); + c = getInstance(MetadataServices.class, ServiceLoader.load(MetadataServices.class), "sis-metadata"); if (c == null) { c = new MetadataServices(); }