From 228e980cc894878fd4fb036b1f0db12fc5fad2b5 Mon Sep 17 00:00:00 2001 From: artem-v Date: Sat, 15 Feb 2020 17:05:03 +0200 Subject: [PATCH 1/4] WIP --- .../examples/ConfigRegistryExample.java | 4 +- .../scalecube/config/examples/DemoConfig.java | 4 +- .../examples/LocalResourceConfigExample.java | 4 +- .../PredicateOrderingConfigExample.java | 6 +- .../ReloadableLocalResourceConfigExample.java | 9 +- .../audit/Slf4JConfigEventListener.java | 5 +- .../config/source/ClassPathConfigSource.java | 69 +++++++---- .../config/source/ConfigSourceInfo.java | 2 +- .../config/source/DirectoryConfigSource.java | 95 --------------- .../source/FileDirectoryConfigSource.java | 114 ++++++++++++++++++ .../source/FilteredPathConfigSource.java | 27 ++++- .../config/utils/ConfigCollectorUtil.java | 45 ------- 12 files changed, 203 insertions(+), 181 deletions(-) delete mode 100644 config/src/main/java/io/scalecube/config/source/DirectoryConfigSource.java create mode 100644 config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java delete mode 100644 config/src/main/java/io/scalecube/config/utils/ConfigCollectorUtil.java diff --git a/config-examples/src/main/java/io/scalecube/config/examples/ConfigRegistryExample.java b/config-examples/src/main/java/io/scalecube/config/examples/ConfigRegistryExample.java index ded86c46..57a5bfaf 100644 --- a/config-examples/src/main/java/io/scalecube/config/examples/ConfigRegistryExample.java +++ b/config-examples/src/main/java/io/scalecube/config/examples/ConfigRegistryExample.java @@ -6,7 +6,7 @@ import io.scalecube.config.audit.Slf4JConfigEventListener; import io.scalecube.config.http.server.ConfigRegistryHttpServer; import io.scalecube.config.source.ClassPathConfigSource; -import io.scalecube.config.source.DirectoryConfigSource; +import io.scalecube.config.source.FileDirectoryConfigSource; import java.nio.file.Path; import java.util.function.Predicate; @@ -28,7 +28,7 @@ public static void main(String[] args) { ConfigRegistrySettings.builder() .addLastSource("classpath", new ClassPathConfigSource(propsPredicate)) .addLastSource( - "configDirectory", new DirectoryConfigSource(basePath, propsPredicate)) + "configDirectory", new FileDirectoryConfigSource(basePath, propsPredicate)) .addListener(new Slf4JConfigEventListener()) .jmxEnabled(true) .jmxMBeanName("config.exporter:name=ConfigRegistry") diff --git a/config-examples/src/main/java/io/scalecube/config/examples/DemoConfig.java b/config-examples/src/main/java/io/scalecube/config/examples/DemoConfig.java index 4b4c8364..8adf4958 100644 --- a/config-examples/src/main/java/io/scalecube/config/examples/DemoConfig.java +++ b/config-examples/src/main/java/io/scalecube/config/examples/DemoConfig.java @@ -9,7 +9,7 @@ import io.scalecube.config.mongo.MongoConfigConnector; import io.scalecube.config.mongo.MongoConfigEventListener; import io.scalecube.config.mongo.MongoConfigRepository; -import io.scalecube.config.source.DirectoryConfigSource; +import io.scalecube.config.source.FileDirectoryConfigSource; import java.nio.file.Path; import java.util.function.Predicate; @@ -45,7 +45,7 @@ public static void main(String[] args) { ConfigRegistry.create( ConfigRegistrySettings.builder() .addLastSource( - "ConfigDirectory", new DirectoryConfigSource(basePath, propsPredicate)) + "ConfigDirectory", new FileDirectoryConfigSource(basePath, propsPredicate)) .addLastSource("MongoConfig", mongoConfigSource) .addListener(new Slf4JConfigEventListener()) .addListener(new MongoConfigEventListener(connector, auditLogCollectionName)) diff --git a/config-examples/src/main/java/io/scalecube/config/examples/LocalResourceConfigExample.java b/config-examples/src/main/java/io/scalecube/config/examples/LocalResourceConfigExample.java index 637fdf51..eaaa8ded 100644 --- a/config-examples/src/main/java/io/scalecube/config/examples/LocalResourceConfigExample.java +++ b/config-examples/src/main/java/io/scalecube/config/examples/LocalResourceConfigExample.java @@ -4,7 +4,7 @@ import io.scalecube.config.ConfigRegistrySettings; import io.scalecube.config.StringConfigProperty; import io.scalecube.config.source.ClassPathConfigSource; -import io.scalecube.config.source.DirectoryConfigSource; +import io.scalecube.config.source.FileDirectoryConfigSource; import java.nio.file.Path; import java.util.function.Predicate; @@ -26,7 +26,7 @@ public static void main(String[] args) { ConfigRegistrySettings.builder() .addLastSource("classpath", new ClassPathConfigSource(propsPredicate)) .addLastSource( - "configDirectory", new DirectoryConfigSource(basePath, propsPredicate)) + "configDirectory", new FileDirectoryConfigSource(basePath, propsPredicate)) .jmxEnabled(false) .build()); diff --git a/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java b/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java index bce9ae63..2f5d1058 100644 --- a/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java +++ b/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java @@ -7,6 +7,8 @@ import io.scalecube.config.source.SystemPropertiesConfigSource; import java.nio.file.Path; import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; @SuppressWarnings("OptionalGetWithoutIsPresent") public class PredicateOrderingConfigExample { @@ -38,7 +40,9 @@ public static void main(String[] args) { new SystemPropertiesConfigSource(new ClassPathConfigSource(customSysPredicate))) .addLastSource( "classpath", - new ClassPathConfigSource(firstPredicate, secondPredicate, rootPredicate)) + new ClassPathConfigSource( + Stream.of(firstPredicate, secondPredicate, rootPredicate) + .collect(Collectors.toList()))) .build()); StringConfigProperty orderedProp1 = configRegistry.stringProperty("orderedProp1"); diff --git a/config-examples/src/main/java/io/scalecube/config/examples/ReloadableLocalResourceConfigExample.java b/config-examples/src/main/java/io/scalecube/config/examples/ReloadableLocalResourceConfigExample.java index 4b9ccce1..688cbbc1 100644 --- a/config-examples/src/main/java/io/scalecube/config/examples/ReloadableLocalResourceConfigExample.java +++ b/config-examples/src/main/java/io/scalecube/config/examples/ReloadableLocalResourceConfigExample.java @@ -7,7 +7,7 @@ import io.scalecube.config.ObjectConfigProperty; import io.scalecube.config.StringConfigProperty; import io.scalecube.config.audit.Slf4JConfigEventListener; -import io.scalecube.config.source.DirectoryConfigSource; +import io.scalecube.config.source.FileDirectoryConfigSource; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -18,6 +18,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; @SuppressWarnings("OptionalGetWithoutIsPresent") public class ReloadableLocalResourceConfigExample { @@ -39,7 +41,10 @@ public static void main(String[] args) throws Exception { ConfigRegistrySettings.builder() .addLastSource( "configDirectory", - new DirectoryConfigSource(basePath, reloadablePropsPredicate, propsPredicate)) + new FileDirectoryConfigSource( + basePath, + Stream.of(reloadablePropsPredicate, propsPredicate) + .collect(Collectors.toList()))) .addListener(new Slf4JConfigEventListener()) .reloadIntervalSec(1) .build()); diff --git a/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java b/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java index 476f854f..dbd78689 100644 --- a/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java +++ b/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java @@ -14,8 +14,7 @@ public void onEvents(Collection events) { if (!events.isEmpty()) { StringBuilder sb = new StringBuilder(); sb.append("["); - events - .stream() + events.stream() .sorted(Comparator.comparing(ConfigEvent::getName)) .forEach( event -> { @@ -30,7 +29,7 @@ public void onEvents(Collection events) { sb.append(originAsString(event)); }); sb.append("\n").append("]"); - LOGGER.info("Config property changed: {}", sb); + LOGGER.info("Config properties: {}", sb); } } diff --git a/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java b/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java index 95f98983..8b4cddff 100644 --- a/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java +++ b/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java @@ -1,7 +1,6 @@ package io.scalecube.config.source; import io.scalecube.config.ConfigProperty; -import io.scalecube.config.utils.ConfigCollectorUtil; import io.scalecube.config.utils.ThrowableUtil; import java.io.File; import java.io.IOException; @@ -13,7 +12,6 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; @@ -23,41 +21,59 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.StringJoiner; import java.util.TreeMap; import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.regex.Pattern; +import java.util.stream.Collectors; public final class ClassPathConfigSource extends FilteredPathConfigSource { - private final ClassLoader classLoader; - private Map loadedConfig; /** - * Creates provider of configuration properties with classpath as source. + * Constructor. * - * @param classLoader class loader - * @param predicates list of predicates to filter + * @param predicate predicate to match configuration files */ - public ClassPathConfigSource(ClassLoader classLoader, List> predicates) { - super(predicates); - this.classLoader = - Objects.requireNonNull(classLoader, "ClassPathConfigSource: classloader is required"); + public ClassPathConfigSource(Predicate predicate) { + this(Collections.singletonList(predicate)); } + /** + * Constructor. + * + * @param predicates list of predicates to match configuration files + */ public ClassPathConfigSource(List> predicates) { - this(ClassPathConfigSource.class.getClassLoader(), predicates); + super(predicates); } - @SafeVarargs - public ClassPathConfigSource(ClassLoader classLoader, Predicate... predicates) { - super(Arrays.asList(predicates)); - this.classLoader = classLoader; - } + /** + * Factory method to create {@code ClassPathConfigSource} instance by configuration file mask plus + * its prefixes. + * + * @param mask mask for template of configuration property file + * @param prefixes list of prefixes (comma separated list of strings) + * @return new {@code ClassPathConfigSource} instance + */ + public static ClassPathConfigSource createWithPattern(String mask, List prefixes) { + Objects.requireNonNull(mask, "ClassPathConfigSource: mask is required"); + Objects.requireNonNull(prefixes, "ClassPathConfigSource: prefixes is required"); + + Pattern pattern = Pattern.compile(mask); + + Predicate patternPredicate = + path -> pattern.matcher(path.getFileName().toString()).matches(); - @SafeVarargs - public ClassPathConfigSource(Predicate... predicates) { - this(ClassPathConfigSource.class.getClassLoader(), Arrays.asList(predicates)); + List> predicates = + prefixes.stream() + .map(p -> (Predicate) path -> path.getFileName().startsWith(p)) + .map(patternPredicate::and) + .collect(Collectors.toList()); + + return new ClassPathConfigSource(predicates); } @Override @@ -67,7 +83,7 @@ public Map loadConfig() { } Collection pathCollection = new ArrayList<>(); - getClassPathEntries(classLoader).stream() + getClassPathEntries(getClass().getClassLoader()).stream() .filter(uri -> uri.getScheme().equals("file")) .forEach( uri -> { @@ -85,12 +101,11 @@ public Map loadConfig() { } }); - Map> configMap = loadConfigMap(pathCollection); - Map result = new TreeMap<>(); - ConfigCollectorUtil.filterAndCollectInOrder( + + filterAndCollectInOrder( predicates.iterator(), - configMap, + loadConfigMap(pathCollection), (path, map) -> map.entrySet() .forEach( @@ -174,6 +189,8 @@ private void scanJar(File file, Collection collector) throws IOException { @Override public String toString() { - return "ClassPathConfigSource{" + "classLoader=" + classLoader + '}'; + return new StringJoiner(", ", ClassPathConfigSource.class.getSimpleName() + "[", "]") + .add("classLoader=" + getClass().getClassLoader()) + .toString(); } } diff --git a/config/src/main/java/io/scalecube/config/source/ConfigSourceInfo.java b/config/src/main/java/io/scalecube/config/source/ConfigSourceInfo.java index a345400c..e74e3fe4 100644 --- a/config/src/main/java/io/scalecube/config/source/ConfigSourceInfo.java +++ b/config/src/main/java/io/scalecube/config/source/ConfigSourceInfo.java @@ -1,6 +1,6 @@ package io.scalecube.config.source; -public class ConfigSourceInfo { +public final class ConfigSourceInfo { private String sourceName; private int priorityOrder; private String configSourceString; diff --git a/config/src/main/java/io/scalecube/config/source/DirectoryConfigSource.java b/config/src/main/java/io/scalecube/config/source/DirectoryConfigSource.java deleted file mode 100644 index de2b72f0..00000000 --- a/config/src/main/java/io/scalecube/config/source/DirectoryConfigSource.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.scalecube.config.source; - -import io.scalecube.config.ConfigProperty; -import io.scalecube.config.ConfigSourceNotAvailableException; -import io.scalecube.config.utils.ConfigCollectorUtil; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.FileSystemException; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.TreeMap; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public final class DirectoryConfigSource extends FilteredPathConfigSource { - private final Path basePath; - - /** - * Creates provider of configuration properties with directory as source. - * - * @param basePath directory path - * @param predicates list of predicates to filter - */ - public DirectoryConfigSource(String basePath, List> predicates) { - super(predicates); - this.basePath = - Paths.get(Objects.requireNonNull(basePath, "DirectoryConfigSource: basePath is required")); - } - - /** - * Creates provider of configuration properties with directory as source. - * - * @param basePath directory path - * @param predicates predicates to filter - */ - @SafeVarargs - public DirectoryConfigSource(String basePath, Predicate... predicates) { - this(basePath, Arrays.asList(predicates)); - } - - @Override - public Map loadConfig() { - Path basePath1; - try { - basePath1 = basePath.toRealPath(LinkOption.NOFOLLOW_LINKS); - } catch (FileNotFoundException e) { - String message = - String.format("DirectoryConfigSource.basePath='%s' can not be found", basePath); - throw new ConfigSourceNotAvailableException(message, e); - } catch (FileSystemException e) { - String message = - String.format( - "FileSystemException on DirectoryConfigSource.basePath='%s', cause: %s", basePath, e); - throw new ConfigSourceNotAvailableException(message, e); - } catch (IOException e) { - String message = - String.format( - "IOException on DirectoryConfigSource.basePath='%s', cause: %s", basePath, e); - throw new ConfigSourceNotAvailableException(message, e); - } - - File[] files = Optional.ofNullable(basePath1.toFile().listFiles()).orElse(new File[0]); - List pathCollection = Arrays.stream(files).map(File::toPath).collect(Collectors.toList()); - - Map> configMap = loadConfigMap(pathCollection); - - Map result = new TreeMap<>(); - ConfigCollectorUtil.filterAndCollectInOrder( - predicates.iterator(), - configMap, - (path, map) -> - map.entrySet() - .forEach( - entry -> - result.putIfAbsent( - entry.getKey(), - LoadedConfigProperty.withNameAndValue(entry) - .origin(path.toString()) - .build()))); - - return result; - } - - @Override - public String toString() { - return "DirectoryConfigSource{" + "basePath='" + basePath.toAbsolutePath() + "'" + '}'; - } -} diff --git a/config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java b/config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java new file mode 100644 index 00000000..3b5e6488 --- /dev/null +++ b/config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java @@ -0,0 +1,114 @@ +package io.scalecube.config.source; + +import io.scalecube.config.ConfigProperty; +import io.scalecube.config.ConfigSourceNotAvailableException; +import java.io.File; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; +import java.util.TreeMap; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public final class FileDirectoryConfigSource extends FilteredPathConfigSource { + private final Path directory; + + /** + * Constructor. + * + * @param directory directory with configuration files + * @param predicate predicate to match confgitratyion files + */ + public FileDirectoryConfigSource(String directory, Predicate predicate) { + this(directory, Collections.singletonList(predicate)); + } + + /** + * Constructor. + * + * @param directory directory with configuration files + * @param predicates list of predicates to match configuration files + */ + public FileDirectoryConfigSource(String directory, List> predicates) { + super(predicates); + Objects.requireNonNull(directory, "FileDirectoryConfigSource: directory is required"); + this.directory = Paths.get(directory); + } + + /** + * Factory method to create {@code FileDirectoryConfigSource} instance by configuration file mask + * plus its prefixes. + * + * @param directory directory with configuration files + * @param mask mask for template of configuration property file + * @param prefixes list of prefixes (comma separated list of strings) + * @return new {@code FileDirectoryConfigSource} instance + */ + public static FileDirectoryConfigSource createWithPattern( + String directory, String mask, List prefixes) { + Objects.requireNonNull(directory, "FileDirectoryConfigSource: directory is required"); + Objects.requireNonNull(mask, "FileDirectoryConfigSource: mask is required"); + Objects.requireNonNull(mask, "FileDirectoryConfigSource: prefixes is required"); + + Pattern pattern = Pattern.compile(mask); + + Predicate patternPredicate = + path -> pattern.matcher(path.getFileName().toString()).matches(); + + List> predicates = + prefixes.stream() + .map(p -> (Predicate) path -> path.getFileName().startsWith(p)) + .map(patternPredicate::and) + .collect(Collectors.toList()); + + return new FileDirectoryConfigSource(directory, predicates); + } + + @Override + public Map loadConfig() { + Path realDirectory; + try { + realDirectory = directory.toRealPath(LinkOption.NOFOLLOW_LINKS); + } catch (Exception e) { + String message = + String.format( + "Exception at FileDirectoryConfigSource (directory='%s'), cause: %s", directory, e); + throw new ConfigSourceNotAvailableException(message, e); + } + + File[] files = Optional.ofNullable(realDirectory.toFile().listFiles()).orElse(new File[0]); + List pathCollection = Arrays.stream(files).map(File::toPath).collect(Collectors.toList()); + + Map result = new TreeMap<>(); + + filterAndCollectInOrder( + predicates.iterator(), + loadConfigMap(pathCollection), + (path, map) -> + map.entrySet() + .forEach( + entry -> + result.putIfAbsent( + entry.getKey(), + LoadedConfigProperty.withNameAndValue(entry) + .origin(path.toString()) + .build()))); + + return result; + } + + @Override + public String toString() { + return new StringJoiner(", ", FileDirectoryConfigSource.class.getSimpleName() + "[", "]") + .add("directory=" + directory) + .toString(); + } +} diff --git a/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java b/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java index 4a6f2c17..6793623c 100644 --- a/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java +++ b/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java @@ -7,10 +7,12 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.stream.Collectors; import org.slf4j.Logger; @@ -27,12 +29,33 @@ protected FilteredPathConfigSource(List> predicates) { } protected final Map> loadConfigMap(Collection pathCollection) { - return pathCollection - .stream() + return pathCollection.stream() .filter(path -> predicates.stream().anyMatch(predicate -> predicate.test(path))) .collect(Collectors.toMap(path -> path, this::loadProperties)); } + protected final void filterAndCollectInOrder( + Iterator> predicateIterator, + Map> configMap, + BiConsumer> configCollector) { + + if (!predicateIterator.hasNext()) { + return; + } + + Predicate groupPredicate = predicateIterator.next(); + List groups = + configMap.keySet().stream().filter(groupPredicate).collect(Collectors.toList()); + for (Path group : groups) { + Map map = configMap.get(group); + if (!map.isEmpty()) { + configCollector.accept(group, map); + } + } + + filterAndCollectInOrder(predicateIterator, configMap, configCollector); + } + private Map loadProperties(Path input) { try (InputStream is = input.toUri().toURL().openStream()) { Properties properties = new Properties(); diff --git a/config/src/main/java/io/scalecube/config/utils/ConfigCollectorUtil.java b/config/src/main/java/io/scalecube/config/utils/ConfigCollectorUtil.java deleted file mode 100644 index 86acfa01..00000000 --- a/config/src/main/java/io/scalecube/config/utils/ConfigCollectorUtil.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.scalecube.config.utils; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public final class ConfigCollectorUtil { - - private ConfigCollectorUtil() { - // Do not instantiate - } - - /** - * Performs filtering and collecting configuration properties. - * - * @param predicateIterator iterator over collection of predicates - * @param configMap configuration map - * @param configCollector collector function - * @param type of the input to the predicate - */ - public static void filterAndCollectInOrder( - Iterator> predicateIterator, - Map> configMap, - BiConsumer> configCollector) { - - if (!predicateIterator.hasNext()) { - return; - } - - Predicate groupPredicate = predicateIterator.next(); - List groups = - configMap.keySet().stream().filter(groupPredicate).collect(Collectors.toList()); - for (T group : groups) { - Map map = configMap.get(group); - if (!map.isEmpty()) { - configCollector.accept(group, map); - } - } - - filterAndCollectInOrder(predicateIterator, configMap, configCollector); - } -} From f16438b3b3d5ef6be197c01c8bf233fc933ac64f Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Mon, 17 Feb 2020 10:34:25 +0200 Subject: [PATCH 2/4] Fixed classpath config source --- .../config/source/ClassPathConfigSource.java | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java b/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java index 95f98983..639b9703 100644 --- a/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java +++ b/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java @@ -5,6 +5,7 @@ import io.scalecube.config.utils.ThrowableUtil; import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -17,7 +18,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Enumeration; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -27,8 +27,12 @@ import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.stream.Collectors; public final class ClassPathConfigSource extends FilteredPathConfigSource { + private static final String CLASSPATH = System.getProperty("java.class.path"); + private static final String PATH_SEPARATOR = System.getProperty("path.separator"); + private final ClassLoader classLoader; private Map loadedConfig; @@ -104,24 +108,55 @@ public Map loadConfig() { return loadedConfig = result; } - private Collection getClassPathEntries(ClassLoader classLoader) { + static Collection getClassPathEntries(ClassLoader classloader) { Collection entries = new LinkedHashSet<>(); - // Search parent first, since it's the order ClassLoader#loadClass() uses. - ClassLoader parent = classLoader.getParent(); + ClassLoader parent = classloader.getParent(); if (parent != null) { entries.addAll(getClassPathEntries(parent)); } - if (classLoader instanceof URLClassLoader) { - URLClassLoader urlClassLoader = (URLClassLoader) classLoader; - for (URL entry : urlClassLoader.getURLs()) { + for (URL url : getClassLoaderUrls(classloader)) { + if (url.getProtocol().equals("file")) { + entries.add(toFile(url).toURI()); + } + } + return new LinkedHashSet<>(entries); + } + + private static File toFile(URL url) { + if (!url.getProtocol().equals("file")) { + throw new IllegalArgumentException("Unsupported protocol in url: " + url); + } + try { + return new File(url.toURI()); + } catch (URISyntaxException e) { + return new File(url.getPath()); + } + } + + private static Collection getClassLoaderUrls(ClassLoader classloader) { + if (classloader instanceof URLClassLoader) { + return Arrays.stream(((URLClassLoader) classloader).getURLs()).collect(Collectors.toSet()); + } + if (classloader.equals(ClassLoader.getSystemClassLoader())) { + return parseJavaClassPath(); + } + return Collections.emptySet(); + } + + private static Collection parseJavaClassPath() { + Collection urls = new LinkedHashSet<>(); + for (String entry : CLASSPATH.split(PATH_SEPARATOR)) { + try { try { - entries.add(entry.toURI()); - } catch (URISyntaxException e) { - throw ThrowableUtil.propagate(e); + urls.add(new File(entry).toURI().toURL()); + } catch (SecurityException e) { + urls.add(new URL("file", null, new File(entry).getAbsolutePath())); } + } catch (MalformedURLException ex) { + throw ThrowableUtil.propagate(ex); } } - return new LinkedHashSet<>(entries); + return new LinkedHashSet<>(urls); } private void scanDirectory( @@ -129,15 +164,13 @@ private void scanDirectory( throws IOException { File canonical = directory.getCanonicalFile(); if (ancestors.contains(canonical)) { - // A cycle in the filesystem, for example due to a symbolic link. return; } File[] files = directory.listFiles(); if (files == null) { return; } - HashSet objects = new HashSet<>(); - objects.addAll(ancestors); + Set objects = new LinkedHashSet<>(ancestors); objects.add(canonical); Set newAncestors = Collections.unmodifiableSet(objects); for (File f : files) { From 92a58c9e97bc5819ae14a866695fe86a0d94c030 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Mon, 17 Feb 2020 10:49:55 +0200 Subject: [PATCH 3/4] Fixed classpath config source --- .../io/scalecube/config/audit/Slf4JConfigEventListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java b/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java index dbd78689..f7643fa9 100644 --- a/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java +++ b/config/src/main/java/io/scalecube/config/audit/Slf4JConfigEventListener.java @@ -29,7 +29,7 @@ public void onEvents(Collection events) { sb.append(originAsString(event)); }); sb.append("\n").append("]"); - LOGGER.info("Config properties: {}", sb); + LOGGER.info(sb.toString()); } } From 6c42f7889214d53bea224e5163c6e2b85941a673 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Mon, 17 Feb 2020 12:03:32 +0200 Subject: [PATCH 4/4] Finished woith pattern predicatse utilities --- .../PredicateOrderingConfigExample.java | 6 +-- .../PredicateShortcutsConfigExample.java | 54 +++++++++++++++++++ .../config/source/ClassPathConfigSource.java | 23 ++------ .../source/FileDirectoryConfigSource.java | 19 +------ .../source/FilteredPathConfigSource.java | 27 ++++++---- 5 files changed, 81 insertions(+), 48 deletions(-) create mode 100644 config-examples/src/main/java/io/scalecube/config/examples/PredicateShortcutsConfigExample.java diff --git a/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java b/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java index 2f5d1058..089fe6c9 100644 --- a/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java +++ b/config-examples/src/main/java/io/scalecube/config/examples/PredicateOrderingConfigExample.java @@ -46,9 +46,9 @@ public static void main(String[] args) { .build()); StringConfigProperty orderedProp1 = configRegistry.stringProperty("orderedProp1"); - String foo = configRegistry.stringValue("foo", null); - String bar = configRegistry.stringValue("bar", null); - String sysFoo = configRegistry.stringValue("sys.foo", null); + String foo = configRegistry.stringProperty("foo").valueOrThrow(); + String bar = configRegistry.stringProperty("bar").valueOrThrow(); + String sysFoo = configRegistry.stringProperty("sys.foo").valueOrThrow(); System.out.println( "### Matched by first predicate: orderedProp1=" + orderedProp1.value().get()); diff --git a/config-examples/src/main/java/io/scalecube/config/examples/PredicateShortcutsConfigExample.java b/config-examples/src/main/java/io/scalecube/config/examples/PredicateShortcutsConfigExample.java new file mode 100644 index 00000000..7068223c --- /dev/null +++ b/config-examples/src/main/java/io/scalecube/config/examples/PredicateShortcutsConfigExample.java @@ -0,0 +1,54 @@ +package io.scalecube.config.examples; + +import io.scalecube.config.ConfigRegistry; +import io.scalecube.config.ConfigRegistrySettings; +import io.scalecube.config.StringConfigProperty; +import io.scalecube.config.source.ClassPathConfigSource; +import io.scalecube.config.source.SystemPropertiesConfigSource; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SuppressWarnings("OptionalGetWithoutIsPresent") +public class PredicateShortcutsConfigExample { + + /** + * Main method for example of predicate ordering. + * + * @param args program arguments + */ + public static void main(String[] args) { + // Emulate scenario where sys.foo was also given from system properties + // System.setProperty("sys.foo", "sys foo from java system properties"); + + String mask = ".*config\\.props"; + + ConfigRegistry configRegistry = + ConfigRegistry.create( + ConfigRegistrySettings.builder() + .addLastSource("sysProps", new SystemPropertiesConfigSource()) + .addLastSource( + "customSysProps", + new SystemPropertiesConfigSource( + ClassPathConfigSource.createWithPattern( + mask, Stream.of("customSys").collect(Collectors.toList())))) + .addLastSource( + "classpath", + ClassPathConfigSource.createWithPattern( + mask, Stream.of("order1", "order2").collect(Collectors.toList()))) + .build()); + + StringConfigProperty orderedProp1 = configRegistry.stringProperty("orderedProp1"); + String foo = configRegistry.stringProperty("foo").valueOrThrow(); + String bar = configRegistry.stringProperty("bar").valueOrThrow(); + String sysFoo = configRegistry.stringProperty("sys.foo").valueOrThrow(); + + System.out.println( + "### Matched by first predicate: orderedProp1=" + orderedProp1.value().get()); + System.out.println("### Regardeless of predicates: foo=" + foo + ", bar=" + bar); + System.out.println( + "### Custom system property: sysFoo=" + + sysFoo + + ", System.getProperty(sysFoo)=" + + System.getProperty("sys.foo")); + } +} diff --git a/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java b/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java index 31963089..338d0ebf 100644 --- a/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java +++ b/config/src/main/java/io/scalecube/config/source/ClassPathConfigSource.java @@ -27,7 +27,6 @@ import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.regex.Pattern; import java.util.stream.Collectors; public final class ClassPathConfigSource extends FilteredPathConfigSource { @@ -65,19 +64,7 @@ public ClassPathConfigSource(List> predicates) { public static ClassPathConfigSource createWithPattern(String mask, List prefixes) { Objects.requireNonNull(mask, "ClassPathConfigSource: mask is required"); Objects.requireNonNull(prefixes, "ClassPathConfigSource: prefixes is required"); - - Pattern pattern = Pattern.compile(mask); - - Predicate patternPredicate = - path -> pattern.matcher(path.getFileName().toString()).matches(); - - List> predicates = - prefixes.stream() - .map(p -> (Predicate) path -> path.getFileName().startsWith(p)) - .map(patternPredicate::and) - .collect(Collectors.toList()); - - return new ClassPathConfigSource(predicates); + return new ClassPathConfigSource(preparePatternPredicates(mask, prefixes)); } @Override @@ -106,7 +93,6 @@ public Map loadConfig() { }); Map result = new TreeMap<>(); - filterAndCollectInOrder( predicates.iterator(), loadConfigMap(pathCollection), @@ -119,11 +105,10 @@ public Map loadConfig() { LoadedConfigProperty.withNameAndValue(entry) .origin(path.toString()) .build()))); - return loadedConfig = result; } - static Collection getClassPathEntries(ClassLoader classloader) { + private static Collection getClassPathEntries(ClassLoader classloader) { Collection entries = new LinkedHashSet<>(); ClassLoader parent = classloader.getParent(); if (parent != null) { @@ -174,7 +159,7 @@ private static Collection parseJavaClassPath() { return new LinkedHashSet<>(urls); } - private void scanDirectory( + private static void scanDirectory( File directory, String prefix, Set ancestors, Collection collector) throws IOException { File canonical = directory.getCanonicalFile(); @@ -198,7 +183,7 @@ private void scanDirectory( } } - private void scanJar(File file, Collection collector) throws IOException { + private static void scanJar(File file, Collection collector) throws IOException { JarFile jarFile; try { jarFile = new JarFile(file); diff --git a/config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java b/config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java index 3b5e6488..4e0ff7cf 100644 --- a/config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java +++ b/config/src/main/java/io/scalecube/config/source/FileDirectoryConfigSource.java @@ -15,7 +15,6 @@ import java.util.StringJoiner; import java.util.TreeMap; import java.util.function.Predicate; -import java.util.regex.Pattern; import java.util.stream.Collectors; public final class FileDirectoryConfigSource extends FilteredPathConfigSource { @@ -56,20 +55,8 @@ public static FileDirectoryConfigSource createWithPattern( String directory, String mask, List prefixes) { Objects.requireNonNull(directory, "FileDirectoryConfigSource: directory is required"); Objects.requireNonNull(mask, "FileDirectoryConfigSource: mask is required"); - Objects.requireNonNull(mask, "FileDirectoryConfigSource: prefixes is required"); - - Pattern pattern = Pattern.compile(mask); - - Predicate patternPredicate = - path -> pattern.matcher(path.getFileName().toString()).matches(); - - List> predicates = - prefixes.stream() - .map(p -> (Predicate) path -> path.getFileName().startsWith(p)) - .map(patternPredicate::and) - .collect(Collectors.toList()); - - return new FileDirectoryConfigSource(directory, predicates); + Objects.requireNonNull(prefixes, "FileDirectoryConfigSource: prefixes is required"); + return new FileDirectoryConfigSource(directory, preparePatternPredicates(mask, prefixes)); } @Override @@ -88,7 +75,6 @@ public Map loadConfig() { List pathCollection = Arrays.stream(files).map(File::toPath).collect(Collectors.toList()); Map result = new TreeMap<>(); - filterAndCollectInOrder( predicates.iterator(), loadConfigMap(pathCollection), @@ -101,7 +87,6 @@ public Map loadConfig() { LoadedConfigProperty.withNameAndValue(entry) .origin(path.toString()) .build()))); - return result; } diff --git a/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java b/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java index 6793623c..65e89af8 100644 --- a/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java +++ b/config/src/main/java/io/scalecube/config/source/FilteredPathConfigSource.java @@ -14,13 +14,11 @@ import java.util.Properties; import java.util.function.BiConsumer; import java.util.function.Predicate; +import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.stream.Stream; public abstract class FilteredPathConfigSource implements ConfigSource { - private static final Logger LOGGER = LoggerFactory.getLogger(FilteredPathConfigSource.class); - protected final List> predicates; protected FilteredPathConfigSource(List> predicates) { @@ -31,10 +29,22 @@ protected FilteredPathConfigSource(List> predicates) { protected final Map> loadConfigMap(Collection pathCollection) { return pathCollection.stream() .filter(path -> predicates.stream().anyMatch(predicate -> predicate.test(path))) - .collect(Collectors.toMap(path -> path, this::loadProperties)); + .collect(Collectors.toMap(path -> path, FilteredPathConfigSource::loadProperties)); + } + + static List> preparePatternPredicates(String mask, List prefixes) { + Pattern pattern = Pattern.compile(mask); + Predicate patternPredicate = path -> pattern.matcher(path.toString()).matches(); + + Stream> stream = + prefixes.stream() + .map(p -> (Predicate) path -> path.getFileName().toString().startsWith(p)) + .map(p -> p.and(patternPredicate)); + + return Stream.concat(stream, Stream.of(patternPredicate)).collect(Collectors.toList()); } - protected final void filterAndCollectInOrder( + static void filterAndCollectInOrder( Iterator> predicateIterator, Map> configMap, BiConsumer> configCollector) { @@ -56,18 +66,17 @@ protected final void filterAndCollectInOrder( filterAndCollectInOrder(predicateIterator, configMap, configCollector); } - private Map loadProperties(Path input) { + private static Map loadProperties(Path input) { try (InputStream is = input.toUri().toURL().openStream()) { Properties properties = new Properties(); properties.load(is); return fromProperties(properties); } catch (Exception e) { - LOGGER.error("Exception at loading props from '{}', cause: {}", input, e); throw ThrowableUtil.propagate(e); } } - private Map fromProperties(Properties properties) { + private static Map fromProperties(Properties properties) { Map map = new HashMap<>(); for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) { String key = (String) e.nextElement();