Skip to content

Commit

Permalink
Refactor SPI interfaces (#149)
Browse files Browse the repository at this point in the history
* refactor SPI

* only static spi

* Revert log changes

* Move all the ServiceLoader use into ConfigServiceLoader

* Rename ConfigSPI to ConfigExtension

* Remove unused META-INF/services/io.avaje.config.ConfigurationPlugin

As this is replaced by the generated one for ConfigExtension

* Move the initalisation of Parsers into ConfigServiceLoader so that it happens once.

* Improve javadoc for ConfigServiceLoader

---------

Co-authored-by: Rob Bygrave <robin.bygrave@gmail.com>
  • Loading branch information
SentryMan and rbygrave authored May 15, 2024
1 parent 89e5d4c commit e7601cb
Show file tree
Hide file tree
Showing 24 changed files with 183 additions and 82 deletions.
9 changes: 8 additions & 1 deletion avaje-aws-appconfig/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,16 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-config</artifactId>
<version>3.15-RC1</version>
<version>4.0-RC1</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-spi-service</artifactId>
<version>1.4</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
package io.avaje.config.appconfig;

import io.avaje.applog.AppLog;
import io.avaje.config.ConfigParser;
import io.avaje.config.Configuration;
import io.avaje.config.ConfigurationSource;
import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.ERROR;
import static java.lang.System.Logger.Level.INFO;
import static java.lang.System.Logger.Level.TRACE;
import static java.lang.System.Logger.Level.WARNING;

import java.io.StringReader;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

import static java.lang.System.Logger.Level.*;
import io.avaje.applog.AppLog;
import io.avaje.config.ConfigParser;
import io.avaje.config.Configuration;
import io.avaje.config.ConfigurationSource;
import io.avaje.spi.ServiceProvider;

/**
* Plugin that loads AWS AppConfig as Yaml or Properties.
* <p>
* By default, will periodically reload the configuration if it has changed.
*/
@ServiceProvider
public final class AppConfigPlugin implements ConfigurationSource {

private static final System.Logger log = AppLog.getLogger("io.avaje.config.AwsAppConfig");
Expand Down
4 changes: 3 additions & 1 deletion avaje-aws-appconfig/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import io.avaje.config.ConfigExtension;
import io.avaje.config.appconfig.AppConfigPlugin;

module io.avaje.config.appconfig {
Expand All @@ -7,5 +8,6 @@
requires io.avaje.config;
requires java.net.http;
requires transitive io.avaje.applog;
provides io.avaje.config.ConfigurationSource with AppConfigPlugin;
requires static io.avaje.spi;
provides ConfigExtension with AppConfigPlugin;
}

This file was deleted.

9 changes: 8 additions & 1 deletion avaje-config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<groupId>io.avaje</groupId>
<artifactId>avaje-config</artifactId>
<version>3.15-RC1</version>
<version>4.0-RC1</version>

<scm>
<developerConnection>scm:git:git@github.com:avaje/avaje-config.git</developerConnection>
Expand All @@ -31,6 +31,13 @@
<version>1.1</version>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-spi-service</artifactId>
<version>1.4</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-applog</artifactId>
Expand Down
23 changes: 23 additions & 0 deletions avaje-config/src/main/java/io/avaje/config/ConfigExtension.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.avaje.config;

import io.avaje.spi.Service;

/**
* Super interface for all extensions to avaje-config which are loaded via ServiceLoader.
* <p>
* Extend avaje-config by implementing one of the following extension interfaces below and
* have it registered with {@link java.util.ServiceLoader} as a {@link ConfigExtension}.
*
* <h4>Extensions</h4>
* <ul>
* <li>{@link ConfigParser}</li>
* <li>{@link ConfigurationLog}</li>
* <li>{@link ConfigurationPlugin}</li>
* <li>{@link ConfigurationSource}</li>
* <li>{@link ResourceLoader}</li>
* <li>{@link ModificationEventRunner}</li>
* </ul>
*/
@Service
public interface ConfigExtension {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Load a config file into a flattened map.
*/
@NonNullApi
public interface ConfigParser {
public interface ConfigParser extends ConfigExtension {

/**
* File extensions Supported by this parser
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.avaje.config;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

/**
* Load all the avaje-config extensions via ServiceLoader using the single
* common ConfigExtension interface.
*/
final class ConfigServiceLoader {

private static final ConfigServiceLoader INSTANCE = new ConfigServiceLoader();

static ConfigServiceLoader get() {
return INSTANCE;
}

private final ConfigurationLog log;
private final ResourceLoader resourceLoader;
private final ModificationEventRunner eventRunner;
private final List<ConfigurationSource> sources = new ArrayList<>();
private final List<ConfigurationPlugin> plugins = new ArrayList<>();
private final Parsers parsers;

ConfigServiceLoader() {
ModificationEventRunner _eventRunner = null;
ConfigurationLog _log = null;
ResourceLoader _resourceLoader = null;
List<ConfigParser> otherParsers = new ArrayList<>();

for (var spi : ServiceLoader.load(ConfigExtension.class)) {
if (spi instanceof ConfigurationSource) {
sources.add((ConfigurationSource) spi);
} else if (spi instanceof ConfigurationPlugin) {
plugins.add((ConfigurationPlugin) spi);
} else if (spi instanceof ConfigParser) {
otherParsers.add((ConfigParser) spi);
} else if (spi instanceof ConfigurationLog) {
_log = (ConfigurationLog) spi;
} else if (spi instanceof ResourceLoader) {
_resourceLoader = (ResourceLoader) spi;
} else if (spi instanceof ModificationEventRunner) {
_eventRunner = (ModificationEventRunner) spi;
}
}

this.log = _log == null ? new DefaultConfigurationLog() : _log;
this.resourceLoader = _resourceLoader == null ? new DefaultResourceLoader() : _resourceLoader;
this.eventRunner = _eventRunner == null ? new CoreConfiguration.ForegroundEventRunner() : _eventRunner;
this.parsers = new Parsers(otherParsers);
}

Parsers parsers() {
return parsers;
}

ConfigurationLog log() {
return log;
}

ResourceLoader resourceLoader() {
return resourceLoader;
}

ModificationEventRunner eventRunner() {
return eventRunner;
}

List<ConfigurationSource> sources() {
return sources;
}

List<ConfigurationPlugin> plugins() {
return plugins;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* control how the events are logged. For example, it might delay logging messages
* until logging implementation has finished configuration.
*/
public interface ConfigurationLog {
public interface ConfigurationLog extends ConfigExtension {

/**
* Invoked when the configuration is being initialised.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* Plugin that is initiated after the configuration has been loaded.
*/
public interface ConfigurationPlugin {
public interface ConfigurationPlugin extends ConfigExtension {

/**
* Apply the plugin. Typically, a plugin might read configuration and do something
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* Additional source to load and update configuration.
*/
public interface ConfigurationSource {
public interface ConfigurationSource extends ConfigExtension {

/**
* Load additional configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ final class CoreComponents {
this.plugins = plugins;
}

/** For testing only */
CoreComponents() {
this.runner = new CoreConfiguration.ForegroundEventRunner();
this.log = new DefaultConfigurationLog();
this.parsers = new Parsers();
this.parsers = new Parsers(Collections.emptyList());
this.sources = Collections.emptyList();
this.plugins = Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import java.io.UncheckedIOException;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.stream.Collectors;

import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.INFO;
Expand All @@ -18,20 +16,15 @@
@NonNullApi
final class CoreConfigurationBuilder implements Configuration.Builder {

private ConfigurationLog log = initialiseLog();
private final Parsers parsers = new Parsers();
private final CoreEntry.CoreMap sourceMap = CoreEntry.newMap();
private ResourceLoader resourceLoader = initialiseResourceLoader();
private ModificationEventRunner eventRunner;
private final ConfigServiceLoader serviceLoader = ConfigServiceLoader.get();
private final Parsers parsers = serviceLoader.parsers();
private ConfigurationLog log = serviceLoader.log();
private ResourceLoader resourceLoader = serviceLoader.resourceLoader();
private ModificationEventRunner eventRunner = serviceLoader.eventRunner();
private boolean includeResourceLoading;
private InitialLoader initialLoader;

private static ConfigurationLog initialiseLog() {
return ServiceLoader.load(ConfigurationLog.class)
.findFirst()
.orElseGet(DefaultConfigurationLog::new);
}

@Override
public Configuration.Builder eventRunner(ModificationEventRunner eventRunner) {
this.eventRunner = requireNonNull(eventRunner);
Expand Down Expand Up @@ -137,15 +130,7 @@ public Configuration.Builder includeResourceLoading() {

@Override
public Configuration build() {
final var runner = initRunner();
final var sources = ServiceLoader.load(ConfigurationSource.class).stream()
.map(ServiceLoader.Provider::get)
.collect(Collectors.toList());
final var plugins = ServiceLoader.load(ConfigurationPlugin.class).stream()
.map(ServiceLoader.Provider::get)
.collect(Collectors.toList());

var components = new CoreComponents(runner, log, parsers, sources, plugins);
var components = new CoreComponents(eventRunner, log, parsers, serviceLoader.sources(), serviceLoader.plugins());
if (includeResourceLoading) {
log.preInitialisation();
initialLoader = new InitialLoader(components, resourceLoader);
Expand All @@ -162,19 +147,4 @@ private CoreEntry.CoreMap initEntries() {
private CoreEntry.CoreMap initEntryMap() {
return initialLoader == null ? CoreEntry.newMap() : initialLoader.load();
}

private static ResourceLoader initialiseResourceLoader() {
return ServiceLoader.load(ResourceLoader.class)
.findFirst()
.orElseGet(DefaultResourceLoader::new);
}

private ModificationEventRunner initRunner() {
if (eventRunner == null) {
eventRunner = ServiceLoader.load(ModificationEventRunner.class)
.findFirst()
.orElseGet(CoreConfiguration.ForegroundEventRunner::new);
}
return eventRunner;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* The default is for event listener notification to be executed using the same thread
* that is making the modifications to the configuration.
*/
public interface ModificationEventRunner {
public interface ModificationEventRunner extends ConfigExtension {

/**
* Run the task of notifying all the event listeners of a modification event
Expand Down
16 changes: 8 additions & 8 deletions avaje-config/src/main/java/io/avaje/config/Parsers.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.avaje.config;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;

/**
Expand All @@ -12,13 +12,13 @@ final class Parsers {

private final Map<String, ConfigParser> parserMap = new HashMap<>();

Parsers() {
Parsers(List<ConfigParser> otherParsers) {
parserMap.put("properties", new PropertiesParser());
if (!"true".equals(System.getProperty("skipYaml"))) {
initYamlParser();
}
if (!"true".equals(System.getProperty("skipCustomParsing"))) {
initParsers();
initParsers(otherParsers);
}
}

Expand All @@ -34,12 +34,12 @@ private void initYamlParser() {
parserMap.put("yaml", yamlLoader);
}

private void initParsers() {
ServiceLoader.load(ConfigParser.class).forEach(p -> {
for (var ext : p.supportedExtensions()) {
parserMap.put(ext, p);
private void initParsers(List<ConfigParser> otherParsers) {
for (ConfigParser parser : otherParsers) {
for (var ext : parser.supportedExtensions()) {
parserMap.put(ext, parser);
}
});
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* Note there is a fallback to use {@link ClassLoader#getSystemResourceAsStream(String)}
* if the ResourceLoader returns null.
*/
public interface ResourceLoader {
public interface ResourceLoader extends ConfigExtension {

/**
* Return the InputStream for the given resource or null if it can not be found.
Expand Down
9 changes: 3 additions & 6 deletions avaje-config/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@
requires transitive io.avaje.applog;
requires static org.yaml.snakeyaml;

uses io.avaje.config.ConfigParser;
uses io.avaje.config.ConfigurationLog;
uses io.avaje.config.ConfigurationPlugin;
uses io.avaje.config.ModificationEventRunner;
uses io.avaje.config.ConfigurationSource;
uses io.avaje.config.ResourceLoader;
requires static io.avaje.spi;

uses io.avaje.config.ConfigExtension;

}
Loading

0 comments on commit e7601cb

Please sign in to comment.