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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new b561bf2d1c Remove the `DefaultFactories` class because it does not
work in the context of JPMS. Instead, callers need to invoke
`java.util.ServiceLoader.load(…)` themselves, because JPMS uses the caller for
checking authorizations.
b561bf2d1c is described below
commit b561bf2d1c370e09b73b2e28737dbeb13ad184e1
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Jun 10 15:18:40 2023 +0200
Remove the `DefaultFactories` class because it does not work in the context
of JPMS.
Instead, callers need to invoke `java.util.ServiceLoader.load(…)`
themselves,
because JPMS uses the caller for checking authorizations.
---
.../apache/sis/internal/jaxb/TypeRegistration.java | 4 +-
.../sis/internal/metadata/ReferencingServices.java | 14 +-
.../apache/sis/internal/metadata/package-info.java | 2 +-
.../sis/internal/metadata/sql/Initializer.java | 18 +-
.../sis/internal/metadata/sql/LocalDataSource.java | 6 +-
.../java/org/apache/sis/xml/MarshallerPool.java | 4 +-
.../sis/internal/jaxb/gml/TimePeriodTest.java | 2 +-
.../apache/sis/internal/referencing/LazySet.java | 4 +-
.../sis/referencing/EPSGFactoryFallback.java | 7 +-
.../sis/referencing/factory/sql/EPSGInstaller.java | 5 +-
.../sis/internal/converter/SystemRegistry.java | 7 +-
.../sis/internal/system/DefaultFactories.java | 213 ---------------------
.../org/apache/sis/internal/system/Reflect.java | 106 ++++++++++
.../internal/temporal/DefaultTemporalFactory.java | 9 +-
.../apache/sis/internal/temporal/package-info.java | 2 +-
.../sis/internal/util/TemporalUtilities.java | 45 +++--
.../apache/sis/setup/InstallationResources.java | 15 +-
.../org/apache/sis/storage/DataStoreRegistry.java | 6 +-
18 files changed, 200 insertions(+), 269 deletions(-)
diff --git
a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
index df04748525..fac23dd33c 100644
---
a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
+++
b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
@@ -29,8 +29,8 @@ import java.lang.ref.WeakReference;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.system.Reflect;
import org.apache.sis.internal.system.SystemListener;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.system.DelayedExecutor;
import org.apache.sis.internal.system.DelayedRunnable;
@@ -163,7 +163,7 @@ public abstract class TypeRegistration {
private static ServiceLoader<TypeRegistration> services() {
ServiceLoader<TypeRegistration> s = services;
if (s == null) {
- services = s =
DefaultFactories.createServiceLoader(TypeRegistration.class);
+ services = s = ServiceLoader.load(TypeRegistration.class,
Reflect.getContextClassLoader());
DelayedExecutor.schedule(new DelayedRunnable(1, TimeUnit.MINUTES) {
@Override public void run() {services = null;}
});
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 84c7e38341..8e297de80f 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
@@ -31,7 +31,6 @@ import
org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
import org.apache.sis.metadata.iso.extent.DefaultTemporalExtent;
import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
import org.apache.sis.metadata.iso.extent.DefaultSpatialTemporalExtent;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.system.OptionalDependency;
import org.apache.sis.internal.system.Modules;
@@ -45,7 +44,7 @@ import org.apache.sis.internal.system.Modules;
* <cite>"referencing by coordinates"</cite> but needed by metadata.</p>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
* @since 0.3
*/
public class ReferencingServices extends OptionalDependency {
@@ -278,20 +277,11 @@ public class ReferencingServices extends
OptionalDependency {
/**
* Returns the default coordinate operation factory.
- * The default implementation is used when the referencing module is not
on the classpath,
- * in which case this method uses whatever implementation is found by the
service loader.
- * Otherwise (if the referencing module is present), this method will be
overridden with
- * the SIS specific implementation.
*
* @return the coordinate operation factory to use.
*/
public CoordinateOperationFactory getCoordinateOperationFactory() {
- final CoordinateOperationFactory factory =
DefaultFactories.forClass(CoordinateOperationFactory.class);
- if (factory != null) {
- return factory;
- } else {
- throw moduleNotFound();
- }
+ throw moduleNotFound();
}
/**
diff --git
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/package-info.java
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/package-info.java
index a825ad016e..3280628ec4 100644
---
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/package-info.java
+++
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/package-info.java
@@ -24,7 +24,7 @@
* may change in incompatible ways in any future version without notice.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
* @since 0.3
*/
package org.apache.sis.internal.metadata;
diff --git
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java
index 45d7946804..b2b2218133 100644
---
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java
+++
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java
@@ -19,13 +19,14 @@ package org.apache.sis.internal.metadata.sql;
import java.util.Locale;
import java.util.function.Supplier;
import java.util.concurrent.Callable;
-import java.io.IOException;
+import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
+import java.io.IOException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
@@ -38,10 +39,10 @@ import javax.naming.event.NamingExceptionEvent;
import javax.naming.event.ObjectChangeListener;
import org.apache.sis.setup.InstallationResources;
import org.apache.sis.internal.system.Configuration;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.system.DataDirectory;
import org.apache.sis.internal.system.SystemListener;
import org.apache.sis.internal.system.Shutdown;
+import org.apache.sis.internal.system.Reflect;
import org.apache.sis.util.resources.Messages;
import org.apache.sis.util.logging.Logging;
@@ -111,6 +112,15 @@ public abstract class Initializer {
protected Initializer() {
}
+ /**
+ * Returns initializers found on the class path.
+ *
+ * @return initializers found on the class path.
+ */
+ public static ServiceLoader<Initializer> load() {
+ return ServiceLoader.load(Initializer.class,
Reflect.getContextClassLoader());
+ }
+
/**
* Invoked for populating an initially empty database.
*
@@ -191,7 +201,7 @@ public abstract class Initializer {
*/
Logging.recoverableException(SystemListener.LOGGER,
Listener.class, "objectChanged", e);
}
- for (Initializer init :
DefaultFactories.createServiceLoader(Initializer.class)) {
+ for (Initializer init : load()) {
init.dataSourceChanged();
}
}
@@ -374,7 +384,7 @@ public abstract class Initializer {
* @since 0.8
*/
private static DataSource embedded() {
- for (InstallationResources res :
DefaultFactories.createServiceLoader(InstallationResources.class)) {
+ for (InstallationResources res : InstallationResources.load()) {
if (res.getAuthorities().contains(EMBEDDED)) try {
final String[] names = res.getResourceNames(EMBEDDED);
for (int i=0; i<names.length; i++) {
diff --git
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/LocalDataSource.java
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/LocalDataSource.java
index b5923c713a..f9a20817ed 100644
---
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/LocalDataSource.java
+++
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/LocalDataSource.java
@@ -33,7 +33,7 @@ import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.internal.system.Loggers;
import org.apache.sis.internal.system.DataDirectory;
-import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.system.Reflect;
import org.apache.sis.internal.util.Strings;
@@ -203,7 +203,7 @@ 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 = DefaultFactories.getContextClassLoader();
+ final ClassLoader loader = Reflect.getContextClassLoader();
final Class<?> c = Class.forName(classname, true, loader);
source = (DataSource) c.getConstructor().newInstance();
final Class<?>[] args = {String.class};
@@ -258,7 +258,7 @@ public final class LocalDataSource implements DataSource,
Comparable<LocalDataSo
default: enabler = null; break;
}
try (Connection c = source.getConnection()) {
- for (Initializer init :
DefaultFactories.createServiceLoader(Initializer.class)) {
+ for (Initializer init : Initializer.load()) {
init.createSchema(c);
}
} finally {
diff --git
a/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java
b/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java
index 80b42769a8..a619e14691 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/MarshallerPool.java
@@ -26,10 +26,10 @@ import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import org.apache.sis.util.logging.Logging;
+import org.apache.sis.internal.system.Reflect;
import org.apache.sis.internal.system.Configuration;
import org.apache.sis.internal.system.DelayedExecutor;
import org.apache.sis.internal.system.DelayedRunnable;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.jaxb.AdapterReplacement;
import org.apache.sis.internal.jaxb.TypeRegistration;
import org.apache.sis.internal.jaxb.Context;
@@ -180,7 +180,7 @@ public class MarshallerPool {
public MarshallerPool(final JAXBContext context, final Map<String,?>
properties) throws JAXBException {
ArgumentChecks.ensureNonNull("context", context);
this.context = context;
- replacements =
DefaultFactories.createServiceLoader(AdapterReplacement.class);
+ replacements = ServiceLoader.load(AdapterReplacement.class,
Reflect.getContextClassLoader());
implementation = Implementation.detect(context);
/*
* Prepares a copy of the property map (if any), then removes the
diff --git
a/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/gml/TimePeriodTest.java
b/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/gml/TimePeriodTest.java
index 9fea8fa905..bd35cbeeb1 100644
---
a/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/gml/TimePeriodTest.java
+++
b/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/gml/TimePeriodTest.java
@@ -68,7 +68,7 @@ public final class TimePeriodTest extends TestCase {
* Creates a GeoAPI instant object for the given date.
*/
private static Instant instant(final String date) {
- return DefaultTemporalFactory.INSTANCE.createInstant(date(date));
+ return DefaultTemporalFactory.provider().createInstant(date(date));
}
/**
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/LazySet.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/LazySet.java
index aa77fb140d..e70e9da650 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/LazySet.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/LazySet.java
@@ -22,7 +22,7 @@ import java.util.Objects;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.NoSuchElementException;
-import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.system.Reflect;
import org.apache.sis.internal.util.SetOfUnknownSize;
import org.apache.sis.internal.util.UnmodifiableArrayList;
@@ -151,7 +151,7 @@ public class LazySet<E> extends SetOfUnknownSize<E> {
*/
private boolean canPullMore() {
if (sourceIterator == null && cachedElements == null) {
- sourceIterator =
DefaultFactories.createServiceLoader(service).iterator();
+ sourceIterator = ServiceLoader.load(service,
Reflect.getContextClassLoader()).iterator();
if (createCache()) {
return true;
}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java
index 7fbbe71697..e50ac160b2 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/EPSGFactoryFallback.java
@@ -18,6 +18,7 @@ package org.apache.sis.referencing;
import java.util.Set;
import java.util.LinkedHashSet;
+import java.util.ServiceLoader;
import javax.measure.Unit;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.NoSuchAuthorityCodeException;
@@ -43,7 +44,6 @@ import org.opengis.metadata.citation.Citation;
import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
import org.apache.sis.internal.referencing.provider.TransverseMercator;
import org.apache.sis.internal.referencing.Resources;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.system.Fallback;
import org.apache.sis.internal.util.MetadataServices;
import org.apache.sis.internal.util.Constants;
@@ -63,7 +63,7 @@ import org.apache.sis.measure.Units;
* The EPSG identifiers are provided as references where to find the complete
definitions.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
* @since 0.7
*/
@Fallback
@@ -364,8 +364,7 @@ final class EPSGFactoryFallback extends
GeodeticAuthorityFactory
private synchronized String getInstallationURL() {
if (installationURL == null) {
installationURL = URLs.EPSG_INSTALL; // To be used as
fallback.
- final Iterable<InstallationResources> services =
-
DefaultFactories.createServiceLoader(InstallationResources.class);
+ final ServiceLoader<InstallationResources> services =
InstallationResources.load();
/*
* Following loop will be executed one or two times. First, we
check for resources that are
* specifically for EPSG geodetic dataset. If none are found,
fallback on embedded database.
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java
index b65c097978..4c93879553 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java
@@ -29,7 +29,6 @@ import java.io.BufferedReader;
import org.apache.sis.util.StringBuilders;
import org.apache.sis.internal.metadata.sql.ScriptRunner;
import org.apache.sis.internal.metadata.sql.SQLUtilities;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.util.StandardDateFormat;
import org.apache.sis.internal.system.Fallback;
import org.apache.sis.util.Exceptions;
@@ -47,7 +46,7 @@ import static org.apache.sis.internal.util.Constants.EPSG;
* in the test directory for more information about how the scripts are
formatted.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
* @since 0.7
*/
final class EPSGInstaller extends ScriptRunner {
@@ -274,7 +273,7 @@ final class EPSGInstaller extends ScriptRunner {
*/
private static InstallationResources lookupProvider(final Locale locale)
throws IOException {
InstallationResources fallback = null;
- for (final InstallationResources provider :
DefaultFactories.createServiceLoader(InstallationResources.class)) {
+ for (final InstallationResources provider :
InstallationResources.load()) {
if (provider.getAuthorities().contains(EPSG)) {
if (!provider.getClass().isAnnotationPresent(Fallback.class)) {
return provider;
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemRegistry.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemRegistry.java
index 679da2ab24..de13b3569c 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemRegistry.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemRegistry.java
@@ -17,12 +17,13 @@
package org.apache.sis.internal.converter;
import java.util.Date;
+import java.util.ServiceLoader;
import org.opengis.util.CodeList;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.UnconvertibleObjectException;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.system.SystemListener;
+import org.apache.sis.internal.system.Reflect;
import org.apache.sis.internal.system.Modules;
@@ -49,7 +50,7 @@ import org.apache.sis.internal.system.Modules;
* The same {@link #INSTANCE} can be safely used by many threads without
synchronization on the part of the caller.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.4
* @since 0.3
*/
public final class SystemRegistry extends ConverterRegistry {
@@ -105,7 +106,7 @@ public final class SystemRegistry extends ConverterRegistry
{
*/
@Override
protected void initialize() {
- for (ObjectConverter<?,?> converter :
DefaultFactories.createServiceLoader(ObjectConverter.class)) {
+ for (ObjectConverter<?,?> converter :
ServiceLoader.load(ObjectConverter.class, Reflect.getContextClassLoader())) {
if (converter instanceof SystemConverter<?,?>) {
converter = ((SystemConverter<?,?>) converter).unique();
}
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java
deleted file mode 100644
index c5e90963cd..0000000000
---
a/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.sis.internal.system;
-
-import java.util.Set;
-import java.util.Map;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.ServiceLoader;
-import java.util.ServiceConfigurationError;
-import java.util.function.Consumer;
-import org.apache.sis.util.logging.Logging;
-
-
-/**
- * Default factories defined in the {@code sis-utility} module.
- * This is a temporary placeholder until we leverage the "dependency
injection" pattern.
- * A candidate replacement is JSR-330.
- *
- * @author Martin Desruisseaux (Geomatys)
- * @author Guilhem Legal (Geomatys)
- * @version 1.4
- *
- * @see <a href="https://jcp.org/en/jsr/detail?id=330">JSR-330</a>
- * @see <a href="https://issues.apache.org/jira/browse/SIS-102">SIS-102</a>
- *
- * @since 0.3
- */
-public final class DefaultFactories extends SystemListener {
- /**
- * Cache of factories found by {@link ServiceLoader} from {@code
META-INF/services} files content.
- */
- private static final Map<Class<?>, Object> FACTORIES = new
IdentityHashMap<>(4);
- static {
- SystemListener.add(new DefaultFactories());
- }
-
- /**
- * For the singleton system listener only.
- */
- private DefaultFactories() {
- super(Modules.UTILITIES);
- }
-
- /**
- * Discards cached factories when the classpath has changed.
- */
- @Override
- protected void classpathChanged() {
- synchronized (DefaultFactories.class) {
- FACTORIES.clear();
- }
- }
-
- /**
- * Returns the default factory implementing the given interface.
- * This method gives preference to Apache SIS implementation of factories
if present.
- * This is a temporary mechanism while we are waiting for a real
dependency injection mechanism.
- *
- * @param <T> the interface type.
- * @param type the interface type.
- * @return a factory implementing the given interface, or {@code null} if
none.
- */
- public static synchronized <T> T forClass(final Class<T> type) {
- T factory = type.cast(FACTORIES.get(type));
- if (factory == null && !FACTORIES.containsKey(type)) {
- T fallback = null;
- for (final T candidate : createServiceLoader(type)) {
- if
(candidate.getClass().getName().startsWith(Modules.CLASSNAME_PREFIX)) {
- if (factory != null) {
- throw new ServiceConfigurationError("Found two
implementations of " + type);
- }
- factory = candidate;
- } else if (fallback == null) {
- fallback = candidate;
- }
- }
- if (factory == null) {
- factory = fallback;
- }
- /*
- * Verifies if the factory that we just selected is the same
implementation than an existing instance.
- * The main case for this test is
org.apache.sis.referencing.factory.GeodeticObjectFactory, where the
- * same class implements 3 factory interfaces.
- */
- if (factory != null) {
- for (final Object existing : FACTORIES.values()) {
- if (existing != null &&
factory.getClass().equals(existing.getClass())) {
- factory = type.cast(existing);
- break;
- }
- }
- }
- FACTORIES.put(type, factory);
- }
- return factory;
- }
-
- /**
- * Returns a service loader for the given type using the default class
loader.
- * The default is the current thread {@linkplain
Thread#getContextClassLoader() context class loader},
- * provided that it can access at least the Apache SIS stores.
- *
- * @param <T> the compile-time value of {@code service} argument.
- * @param service the interface or abstract class representing the
service.
- * @return a new service loader for the given service type.
- *
- * @since 0.8
- */
- public static <T> ServiceLoader<T> createServiceLoader(final Class<T>
service) {
- try {
- return ServiceLoader.load(service, getContextClassLoader());
- } catch (SecurityException e) {
- /*
- * We were not allowed to invoke
Thread.currentThread().getContextClassLoader().
- * But ServiceLoader.load(Class) may be allowed to, since it is
part of JDK.
- */
- Logging.recoverableException(SystemListener.LOGGER,
DefaultFactories.class, "createServiceLoader", e);
- return ServiceLoader.load(service);
- }
- }
-
- /**
- * Returns the context class loader, but makes sure that it has Apache SIS
on its classpath.
- * First, this method invokes {@link Thread#getContextClassLoader()} for
the current thread.
- * Then this method scans over all Apache SIS classes on the stack trace.
For each SIS class,
- * its loader is compared to the above-cited context class loader. If the
context class loader
- * is equal or is a child of the SIS loader, then it is left unchanged.
Otherwise the context
- * class loader is replaced by the SIS one.
- *
- * <p>The intent of this method is to ensure that {@link
ServiceLoader#load(Class)} will find the
- * Apache SIS services even in an environment that defined an unsuitable
context class loader.</p>
- *
- * @return the context class loader if suitable, or another class loader
otherwise.
- * @throws SecurityException if this method is not allowed to get the
current thread
- * context class loader or one of its parent.
- *
- * @since 0.8
- */
- public static ClassLoader getContextClassLoader() throws SecurityException
{
- final Walker walker = new Walker();
- return
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((stream)
-> {
- stream.forEach(walker);
- return walker.loader;
- });
- }
-
- /**
- * Action to be executed for each stack frame inspected by {@link
#getContextClassLoader()}.
- * The action is initialized to the context class loader of current thread.
- * Then it checks if the class loader should be replaced by another one
containing at least
- * the Apache SIS class loader.
- */
- private static final class Walker implements
Consumer<StackWalker.StackFrame> {
- /**
- * The context class loader to be returned by {@link
#getContextClassLoader()}.
- */
- ClassLoader loader;
-
- /**
- * All parents of {@link #loader}.
- */
- private final Set<ClassLoader> parents;
-
- /**
- * Creates a new walker initialized to the context class loader of
current thread.
- */
- Walker() {
- parents = new HashSet<>();
- setClassLoader(Thread.currentThread().getContextClassLoader());
- }
-
- /**
- * Set the class loader to the given value, which may be null.
- */
- private void setClassLoader(ClassLoader c) {
- loader = c;
- while (c != null) {
- parents.add(c);
- c = c.getParent();
- }
- }
-
- /**
- * If the given stack frame is an Apache SIS method, ensures that
{@link #loader}
- * is the SIS class loader or has the SIS class loader as a parent.
- */
- @Override
- public void accept(final StackWalker.StackFrame frame) {
- if (frame.getClassName().startsWith(Modules.CLASSNAME_PREFIX)) {
- ClassLoader c = frame.getDeclaringClass().getClassLoader();
- if (!parents.contains(c)) {
- parents.clear();
- setClassLoader(c);
- }
- }
- }
- }
-}
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/internal/system/Reflect.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/system/Reflect.java
new file mode 100644
index 0000000000..35808d7986
--- /dev/null
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/Reflect.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.system;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.ServiceLoader;
+import java.util.function.Consumer;
+
+
+/**
+ * Contextual information fetching by reflection.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @author Guilhem Legal (Geomatys)
+ * @version 1.4
+ * @since 0.8
+ */
+public final class Reflect implements Consumer<StackWalker.StackFrame> {
+ /**
+ * Returns the context class loader, but makes sure that it has Apache SIS
on its classpath.
+ * First, this method invokes {@link Thread#getContextClassLoader()} for
the current thread.
+ * Then this method scans over all Apache SIS classes on the stack trace.
For each SIS class,
+ * its loader is compared to the above-cited context class loader. If the
context class loader
+ * is equal or is a child of the SIS loader, then it is left unchanged.
Otherwise the context
+ * class loader is replaced by the SIS one.
+ *
+ * <p>The intent of this method is to ensure that {@link
ServiceLoader#load(Class)} will find the
+ * Apache SIS services even in an environment that defined an unsuitable
context class loader.
+ * Note that the call to {@code ServiceLoader.load(…)} must be done from
the caller class.
+ * We cannot provide this convenience in this class because of JPMS
encapsulation.</p>
+ *
+ * @return the context class loader if suitable, or another class loader
otherwise.
+ * @throws SecurityException if this method is not allowed to get the
current thread
+ * context class loader or one of its parent.
+ */
+ public static ClassLoader getContextClassLoader() throws SecurityException
{
+ final Reflect walker = new Reflect();
+ return
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((stream)
-> {
+ stream.forEach(walker);
+ return walker.loader;
+ });
+ }
+
+ /**
+ * The context class loader to be returned by {@link
#getContextClassLoader()}.
+ */
+ private ClassLoader loader;
+
+ /**
+ * All parents of {@link #loader}.
+ */
+ private final Set<ClassLoader> parents;
+
+ /**
+ * Creates a new walker initialized to the context class loader of current
thread.
+ */
+ private Reflect() {
+ parents = new HashSet<>();
+ setClassLoader(Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Set the class loader to the given value, which may be null.
+ */
+ private void setClassLoader(ClassLoader c) {
+ loader = c;
+ while (c != null) {
+ parents.add(c);
+ c = c.getParent();
+ }
+ }
+
+ /**
+ * Action to be executed for each stack frame inspected by {@link
#getContextClassLoader()}.
+ * The action is initialized to the context class loader of current thread.
+ * Then it checks if the class loader should be replaced by another one
+ * containing at least the Apache SIS class loader.
+ *
+ * @param frame a stack frame being inspected.
+ */
+ @Override
+ public void accept(final StackWalker.StackFrame frame) {
+ if (frame.getClassName().startsWith(Modules.CLASSNAME_PREFIX)) {
+ ClassLoader c = frame.getDeclaringClass().getClassLoader();
+ if (!parents.contains(c)) {
+ parents.clear();
+ setClassLoader(c);
+ }
+ }
+ }
+}
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java
index 4f1573343a..c69ef42d31 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java
@@ -32,12 +32,17 @@ import org.apache.sis.util.resources.Errors;
* GeoAPI temporal interfaces are expected to change a lot in a future
revision.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
* @since 1.2
*/
public final class DefaultTemporalFactory implements TemporalFactory {
/** The unique instance of this factory. */
- public static final TemporalFactory INSTANCE = new
DefaultTemporalFactory();
+ private static final TemporalFactory INSTANCE = new
DefaultTemporalFactory();
+
+ /** {@return the unique instance of this factory}. */
+ public static TemporalFactory provider() {
+ return INSTANCE;
+ }
/** Creates the singleton instance. */
private DefaultTemporalFactory() {
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java
index da91b0c8d7..e616a33332 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java
@@ -20,7 +20,7 @@
* the temporal GeoAPI interfaces are expected to change a lot in a future
revision.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
* @since 1.2
*/
package org.apache.sis.internal.temporal;
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
index 4955bc7c17..4d991af9b0 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
@@ -17,12 +17,14 @@
package org.apache.sis.internal.util;
import java.util.Date;
+import java.util.ServiceLoader;
import org.opengis.temporal.Instant;
import org.opengis.temporal.Period;
import org.opengis.temporal.TemporalFactory;
import org.opengis.temporal.TemporalPrimitive;
-import org.apache.sis.util.Static;
-import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.system.Reflect;
+import org.apache.sis.internal.system.SystemListener;
import org.apache.sis.internal.temporal.DefaultTemporalFactory;
@@ -32,28 +34,47 @@ import
org.apache.sis.internal.temporal.DefaultTemporalFactory;
*
* @author Martin Desruisseaux (Geomatys)
* @author Guilhem Legal (Geomatys)
- * @version 1.2
+ * @version 1.4
* @since 0.3
*/
-public final class TemporalUtilities extends Static {
+public final class TemporalUtilities extends SystemListener {
/**
- * Do not allow instantiation of this class.
+ * The default factory to use for implementations.
+ */
+ private static volatile TemporalFactory implementation;
+
+ static {
+ SystemListener.add(new TemporalUtilities());
+ }
+
+ /**
+ * For the singleton system listener only.
*/
private TemporalUtilities() {
+ super(Modules.METADATA);
}
/**
- * Returns a temporal factory if available.
+ * Discards the cached factory when the classpath has changed.
+ */
+ @Override
+ protected void classpathChanged() {
+ implementation = null;
+ }
+
+ /**
+ * Returns a temporal factory, or a default implementation if none.
*
* @return the temporal factory.
- * @throws UnsupportedOperationException if the temporal factory is not
available on the classpath.
*/
- public static TemporalFactory getTemporalFactory() throws
UnsupportedOperationException {
- final TemporalFactory factory =
DefaultFactories.forClass(TemporalFactory.class);
- if (factory != null) {
- return factory;
+ public static TemporalFactory getTemporalFactory() {
+ TemporalFactory factory = implementation;
+ if (factory == null) {
+ factory = ServiceLoader.load(TemporalFactory.class,
Reflect.getContextClassLoader())
+ .findFirst().orElseGet(DefaultTemporalFactory::provider);
+ implementation = factory;
}
- return DefaultTemporalFactory.INSTANCE;
+ return factory;
}
/**
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java
b/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java
index 63c86d07c6..8d12b842cd 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java
@@ -18,11 +18,13 @@ package org.apache.sis.setup;
import java.util.Set;
import java.util.Locale;
+import java.util.ServiceLoader;
import java.io.IOException;
import java.io.BufferedReader;
import org.apache.sis.internal.util.URLs;
import org.apache.sis.internal.util.Constants;
import org.apache.sis.internal.util.MetadataServices;
+import org.apache.sis.internal.system.Reflect;
/**
@@ -66,7 +68,7 @@ import org.apache.sis.internal.util.MetadataServices;
* for writing the database; see above-cited web page for more information).
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
* @since 0.7
*/
public abstract class InstallationResources {
@@ -76,6 +78,17 @@ public abstract class InstallationResources {
protected InstallationResources() {
}
+ /**
+ * Returns installation resources found on the class path.
+ *
+ * @return installation resources found on the class path.
+ *
+ * @since 1.4
+ */
+ public static ServiceLoader<InstallationResources> load() {
+ return ServiceLoader.load(InstallationResources.class,
Reflect.getContextClassLoader());
+ }
+
/**
* Returns identifiers of the resources provided by this instance.
* The values recognized by SIS are listed below
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
index 9f850b5f31..2290c9ad63 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
@@ -21,9 +21,9 @@ import java.util.LinkedList;
import java.util.Set;
import java.util.Iterator;
import java.util.ServiceLoader;
+import org.apache.sis.internal.system.Reflect;
import org.apache.sis.internal.storage.Resources;
import org.apache.sis.internal.storage.StoreMetadata;
-import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.referencing.LazySet;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ArraysExt;
@@ -43,7 +43,7 @@ import org.apache.sis.util.ArraysExt;
* on the part of the caller.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
* @since 0.4
*/
final class DataStoreRegistry {
@@ -60,7 +60,7 @@ final class DataStoreRegistry {
* provided that it can access at least the Apache SIS stores.
*/
public DataStoreRegistry() {
- loader = DefaultFactories.createServiceLoader(DataStoreProvider.class);
+ this(Reflect.getContextClassLoader());
}
/**