Skip to content

Commit

Permalink
Disentangle configuration-impl from configuration-yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
cuioss committed Sep 1, 2023
1 parent 1f4aa53 commit 662cee4
Show file tree
Hide file tree
Showing 25 changed files with 269 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
Expand All @@ -45,14 +44,12 @@
*/
@EqualsAndHashCode
@ToString
public class PropertiesConfigurationProvider implements ConfigurationSource, Serializable {
public class PropertiesConfigurationProvider implements ConfigurationSource {

private static final CuiLogger log = new CuiLogger(PropertiesConfigurationProvider.class);

private static final String PORTAL_503 = "Portal-503: Unable to load from properties file described by '{}', due to: '{}'";

private static final long serialVersionUID = -3827851969415677777L;

private static final String MSG_READ_ERROR = "The referenced file can not be loaded";

@Getter
Expand Down Expand Up @@ -97,7 +94,7 @@ public Map<String, String> getConfigurationMap() {
* errors it will return an empty {@link Properties}
*/
public Optional<Properties> getAsProperties() {
if (null == fileLoader || !fileLoader.isReadable()) {
if ((null == fileLoader) || !fileLoader.isReadable()) {
log.error(PORTAL_503, fileLoader, MSG_READ_ERROR);
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package de.cuioss.portal.configuration.source;

import java.util.Optional;
import java.util.Set;

import de.cuioss.portal.configuration.ConfigurationSource;
import de.cuioss.tools.io.FileLoader;

/**
* SPI for resolving different types of {@link ConfigurationSource}s
*/
public interface ConfigurationSourceResolver {

/**
* @param source must not be null
* @return the concrete {@link ConfigurationSource} if available.
*/
Optional<ConfigurationSource> resolve(FileLoader source);

/**
* @return the supported file-suffixes
*/
Set<String> supportedSuffixes();

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import de.cuioss.test.generator.TypedGenerator;
import de.cuioss.test.valueobjects.ValueObjectTest;
import de.cuioss.test.valueobjects.api.contracts.VerifyConstructor;
import de.cuioss.test.valueobjects.api.object.ObjectTestContracts;
import de.cuioss.test.valueobjects.api.object.VetoObjectTestContract;
import de.cuioss.test.valueobjects.api.property.PropertyConfig;
import de.cuioss.test.valueobjects.api.property.PropertyReflectionConfig;
import de.cuioss.tools.io.FileLoader;
Expand All @@ -33,6 +35,7 @@
@PropertyConfig(name = "fileLoader", propertyClass = FileLoader.class, propertyReadWrite = PropertyReadWrite.WRITE_ONLY)
@VerifyConstructor(of = "fileLoader", required = "fileLoader")
@PropertyReflectionConfig(skip = true)
@VetoObjectTestContract(ObjectTestContracts.SERIALIZABLE)
class PropertiesConfigurationProviderTest extends ValueObjectTest<PropertiesConfigurationProvider>
implements TypedGenerator<FileLoader> {

Expand Down
3 changes: 1 addition & 2 deletions modules/configuration/portal-configuration-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@
<groupId>de.cuioss.portal.configuration</groupId>
<artifactId>portal-configuration-api</artifactId>
</dependency>
<!-- FIXME owolff: Remove this-->
<dependency>
<groupId>de.cuioss.portal.configuration</groupId>
<artifactId>portal-configuration-yaml</artifactId>
<scope>compile</scope>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package de.cuioss.portal.configuration.impl.source;

import static de.cuioss.tools.collect.CollectionLiterals.immutableSet;
import static de.cuioss.tools.string.MoreStrings.isEmpty;

import java.util.Optional;
import java.util.Set;

import de.cuioss.portal.configuration.ConfigurationSource;
import de.cuioss.portal.configuration.impl.PropertiesConfigurationProvider;
import de.cuioss.portal.configuration.source.ConfigurationSourceResolver;
import de.cuioss.tools.io.FileLoader;
import de.cuioss.tools.logging.CuiLogger;

/**
*
*/
public class JavaPropertiesConfigurationSourceResolver implements ConfigurationSourceResolver {

private static final String SUFFIX = "properties";
private static final String MSG_LOAD_ERROR = "Portal-519: Unable to load configuration file: {}, due to: {}";
private static final CuiLogger LOGGER = new CuiLogger(JavaPropertiesConfigurationSourceResolver.class);

@Override
public Optional<ConfigurationSource> resolve(FileLoader source) {
if ((null == source) || isEmpty(source.getFileName().getOriginalName())) {
LOGGER.debug("Nothing to load found");
return Optional.empty();
}

final var suffix = source.getFileName().getSuffix().toLowerCase();
if (SUFFIX.equals(suffix)) {
LOGGER.debug("Found properties file {}", source);
if (!source.isReadable()) {
LOGGER.error(MSG_LOAD_ERROR, source, "not readable");
return Optional.empty();
}
return Optional.of(new PropertiesConfigurationProvider(source));
}

LOGGER.debug("Given file seems not to represent a properties file: {}", source);
return Optional.empty();
}

@Override
public Set<String> supportedSuffixes() {
return immutableSet(SUFFIX);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@
*/
package de.cuioss.portal.configuration.impl.source;

import static de.cuioss.tools.collect.CollectionLiterals.immutableList;
import static de.cuioss.tools.string.MoreStrings.isEmpty;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;

import de.cuioss.portal.configuration.FileConfigurationSource;
import de.cuioss.portal.configuration.impl.PropertiesConfigurationProvider;
import de.cuioss.portal.configuration.yaml.YamlConfigurationProvider;
import de.cuioss.portal.configuration.source.ConfigurationSourceResolver;
import de.cuioss.tools.io.FileLoader;
import de.cuioss.tools.io.FileLoaderUtility;
import de.cuioss.tools.logging.CuiLogger;
import lombok.val;
import lombok.experimental.UtilityClass;

/**
Expand All @@ -38,11 +42,9 @@
@UtilityClass
public class LoaderUtils {

private static final String MSG_LOAD_ERROR = "Portal-519: Unable to load configuration file: {}, due to: {}";

private static final CuiLogger log = new CuiLogger(LoaderUtils.class);

private static final String FILETYPE_NOT_PROVIDED_MSG = "Portal-151: Unsupported configuration file type: {}. Supported is: yml, yaml, properties";
private static final String FILETYPE_NOT_PROVIDED_MSG = "Portal-151: Unsupported configuration file type: {}. Supported is: {}. If you want to support yaml files do not forget to add 'portal-configuration-yaml' to the deployment";

/**
* Loads the the content of a given {@link FileConfigurationSource}
Expand All @@ -52,22 +54,18 @@ public class LoaderUtils {
* @return the {@link Map} of the contained configuration
*/
public static Map<String, String> loadConfigurationFromSource(final FileConfigurationSource source) {
if (null == source || isEmpty(source.getPath())) {
if ((null == source) || isEmpty(source.getPath())) {
log.debug("Nothing to load found");
return Collections.emptyMap();
}

final var ymlProvider = YamlConfigurationProvider.createFromFile(source);
if (ymlProvider.isPresent()) {
return ymlProvider.get().getConfigurationMap();
}

final var propertiesProvider = loadPropertiesFromFile(FileLoaderUtility.getLoaderForPath(source.getPath()));
if (propertiesProvider.isPresent()) {
return propertiesProvider.get().getConfigurationMap();
for (var resolver : loadResolver()) {
var provider = resolver.resolve(FileLoaderUtility.getLoaderForPath(source.getPath()));
if (provider.isPresent()) {
return provider.get().getConfigurationMap();
}
}

log.warn(FILETYPE_NOT_PROVIDED_MSG, source);
log.warn(FILETYPE_NOT_PROVIDED_MSG, source, retrieveSupportedSuffixes());
return Collections.emptyMap();
}

Expand All @@ -79,44 +77,31 @@ public static Map<String, String> loadConfigurationFromSource(final FileConfigur
* @return the {@link Map} of the contained configuration
*/
public static Map<String, String> loadConfigurationFromSource(final FileLoader source) {
final var ymlProvider = YamlConfigurationProvider.createFromFile(source);
if (ymlProvider.isPresent()) {
return ymlProvider.get().getConfigurationMap();
if ((null == source) || isEmpty(source.getFileName().getOriginalName())) {
log.debug("Nothing to load found");
return Collections.emptyMap();
}

final var propertiesProvider = loadPropertiesFromFile(source);
if (propertiesProvider.isPresent()) {
return propertiesProvider.get().getConfigurationMap();
for (var resolver : loadResolver()) {
var provider = resolver.resolve(source);
if (provider.isPresent()) {
return provider.get().getConfigurationMap();
}
}

log.warn(FILETYPE_NOT_PROVIDED_MSG, source);
log.warn(FILETYPE_NOT_PROVIDED_MSG, source, retrieveSupportedSuffixes());
return Collections.emptyMap();
}

/**
* @param source identifying the possible properties file.
*
* @return an {@link Optional} on a {@link PropertiesConfigurationProvider} in
* case the given source references a readable files ending with
* "properties", empty Optional otherwise.
*/
private static Optional<PropertiesConfigurationProvider> loadPropertiesFromFile(final FileLoader source) {
if (null == source || isEmpty(source.getFileName().getOriginalName())) {
log.debug("Nothing to load found");
return Optional.empty();
}
static List<ConfigurationSourceResolver> loadResolver() {
return immutableList(ServiceLoader.load(ConfigurationSourceResolver.class).iterator());
}

final var suffix = source.getFileName().getSuffix().toLowerCase();
if ("properties".equals(suffix)) {
log.debug("Found properties file {}", source);
if (!source.isReadable()) {
log.error(MSG_LOAD_ERROR, source, "not readable");
return Optional.empty();
}
return Optional.of(new PropertiesConfigurationProvider(source));
static Set<String> retrieveSupportedSuffixes() {
val result = new TreeSet<String>();
for (var resolver : loadResolver()) {
result.addAll(resolver.supportedSuffixes());
}

log.debug("Given file seems not to represent a yml file: {}", source);
return Optional.empty();
return result;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Defines the default configuration for the CUI-Portal

# Context parameter within configuration-subsysten with the name "portal.locale.default"
# Defines the default locale. Caution: the default locale must be defined within
# "portal.locale.available".
# The default value for the is: 'de'
portal.locale.default=de

# Context parameter within configuration-subsysten with the name "portal.locale.available"
# Defines the available locales for for the Portal as a comma separated list.
# The default value for the is: 'en,de,fr'
portal.locale.available=en,de,fr

# Path to the Portals configuration directory.
portal.configuration.dir=config/

# Used for the configuration of tracking of changes in files, usually configuration files,
# see FileWatcherService for handling details.
# This is the successor of the previous cron-based approach.
# The default-value is true
portal.configuration.file_watcher.enabled=true

# Project-Stage
#
# Used for the configuration of the Portal project stage. Defaults to 'production'.
# Valid values are: production, test, configuration, development.
# The mapping between Portal stages and their JSF equivalent is as follows
# (Portal-stage -> JSF-stage):
# production -> Production, test -> SystemTest,
# configuration -> Production, development -> Development
portal.configuration.stage=production

# 1 year
portal.storage.cookieMaxAge=31536000

# Defines the maximum interval (in minutes) the user may be inactive
# before we black the screen and display the idle message dialog.
# Should correspond with "timeout". If not set, the timeout value will be used as default.
# Technically this behavior is achieved by setting the "maxInactiveInterval" to the value '-1'.
portal.session.maxInactiveInterval=-1

# Defines the timeout (in minutes) of the session in the jsf server.
portal.session.timeout=20

# Servlet based authentication
portal.authentication.servlet.allowBasicAuth=true

# Keep in sync with 'portal.configuration.stage'
smallrye.config.profile=${portal.configuration.stage}
Loading

0 comments on commit 662cee4

Please sign in to comment.