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

Reply via email to