From bf81142ce0f6416516e0983fa04cac7f03d700f9 Mon Sep 17 00:00:00 2001 From: Gmugra Date: Sun, 9 May 2021 19:17:28 +0200 Subject: [PATCH 1/3] #90 : API: classpath:jar:manifest Loader --- README.md | 4 ++ .../config/core/ConfigFactory.java | 2 + .../loader/ClasspathJarManifestLoader.java | 70 +++++++++++++++++++ .../config/core/util/ApiMessages.java | 3 +- .../config/core/util/ApiMessages.properties | 2 + .../ClasspathJarManifestLoaderTest.java | 54 ++++++++++++++ .../config/core/loader/ConfigHolderTest.java | 4 ++ 7 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java create mode 100644 core/src/test/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoaderTest.java diff --git a/README.md b/README.md index ef38c824..4c28b56d 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,10 @@ FYI: `Converter`-implementation must be stateless and must have a default(no-arg - XML format: [properties.dtd](https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html) or [OWNER](http://owner.aeonbits.org/docs/xml-support/) - Default charset (if URI fragment not present) is **UTF-8** - e.g. `file:./my.xml` +1. META-INF/MANIFEST.MF: classpath:jar:manifest?*attribute*[=value] + - The loader scans all JARs in classpath for META-INF/MANIFEST.MF files. First META-INF/MANIFEST.MF, which contain *attribute* (with optional value) from the URI will be used as source. + - e.g. MANIFEST.MF must containt attribute **Bundle-Name** with value **JUnit Jupiter API**: `classpath:jar:manifest?Bundle-Name=JUnit%20Jupiter%20API` + - e.g. MANIFEST.MF must containt attribute **exotic-unique-attribite** with any value: `classpath:jar:manifest?exotic-unique-attribite` ### Custom loaders It's possible to implement custom loaders using `Loader` interface. diff --git a/core/src/main/java/net/cactusthorn/config/core/ConfigFactory.java b/core/src/main/java/net/cactusthorn/config/core/ConfigFactory.java index 33daa4a5..0c89dd71 100644 --- a/core/src/main/java/net/cactusthorn/config/core/ConfigFactory.java +++ b/core/src/main/java/net/cactusthorn/config/core/ConfigFactory.java @@ -17,6 +17,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import net.cactusthorn.config.core.loader.ClasspathJarManifestLoader; import net.cactusthorn.config.core.loader.ClasspathPropertiesLoader; import net.cactusthorn.config.core.loader.ClasspathXMLLoader; import net.cactusthorn.config.core.loader.ConfigHolder; @@ -60,6 +61,7 @@ private Builder() { loaders.add(new UrlPropertiesLoader()); loaders.add(new ClasspathXMLLoader()); loaders.add(new UrlXMLLoader()); + loaders.add(new ClasspathJarManifestLoader()); } public Builder addLoader(Loader loader) { diff --git a/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java b/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java new file mode 100644 index 00000000..5641c825 --- /dev/null +++ b/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java @@ -0,0 +1,70 @@ +package net.cactusthorn.config.core.loader; + +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import static net.cactusthorn.config.core.util.ApiMessages.msg; +import static net.cactusthorn.config.core.util.ApiMessages.Key.*; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +public class ClasspathJarManifestLoader implements Loader { + + private static final Logger LOG = Logger.getLogger(ClasspathJarManifestLoader.class.getName()); + + private static final String SUB_PREFIX = "jar:manifest?"; + private static final String PREFIX = "classpath:" + SUB_PREFIX; + + @Override public boolean accept(URI uri) { + return uri.toString().startsWith(PREFIX); + } + + @Override public Map load(URI uri, ClassLoader classLoader) { + + String param = uri.getSchemeSpecificPart().substring(SUB_PREFIX.length()); + String[] parts = param.split("="); + String name = parts[0]; + String value = parts.length > 1 ? parts[1] : null; + + try { + Enumeration urls = getClass().getClassLoader().getResources(JarFile.MANIFEST_NAME); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + try (InputStream in = url.openStream()) { + + Manifest manifest = new Manifest(in); + Attributes attributes = manifest.getMainAttributes(); + String attribute = attributes.getValue(name); + if (attribute != null && (value == null || value.equals(attribute))) { + Map result = new HashMap<>(); + for (Map.Entry entry : attributes.entrySet()) { + result.put(entry.getKey().toString(), entry.getValue().toString()); + } + + return result; + } + } + } + } catch (IOException e) { + LOG.info(msg(CANT_LOAD_RESOURCE, uri.toString(), e.toString())); + return Collections.emptyMap(); + } + + if (value != null) { + LOG.info(msg(MANIFEST_NOT_FOUND_1, name, value)); + return Collections.emptyMap(); + } + LOG.info(msg(MANIFEST_NOT_FOUND_2, name)); + return Collections.emptyMap(); + } + +} diff --git a/core/src/main/java/net/cactusthorn/config/core/util/ApiMessages.java b/core/src/main/java/net/cactusthorn/config/core/util/ApiMessages.java index 1451bdf8..9b0532ad 100644 --- a/core/src/main/java/net/cactusthorn/config/core/util/ApiMessages.java +++ b/core/src/main/java/net/cactusthorn/config/core/util/ApiMessages.java @@ -11,7 +11,8 @@ public final class ApiMessages { public enum Key { IS_NULL, IS_EMPTY, CANT_LOAD_RESOURCE, VALUE_NOT_FOUND, LOADER_NOT_FOUND, CANT_INVOKE_CONFIGBUILDER, CANT_FIND_CONFIGBUILDER, - WRONG_SOURCE_PARAM, DURATION_NO_NUMBER, DURATION_WRONG_TIME_UNIT, PERIOD_NO_NUMBER, PERIOD_WRONG_TIME_UNIT + WRONG_SOURCE_PARAM, DURATION_NO_NUMBER, DURATION_WRONG_TIME_UNIT, PERIOD_NO_NUMBER, PERIOD_WRONG_TIME_UNIT, MANIFEST_NOT_FOUND_1, + MANIFEST_NOT_FOUND_2 } private ApiMessages() { diff --git a/core/src/main/resources/net/cactusthorn/config/core/util/ApiMessages.properties b/core/src/main/resources/net/cactusthorn/config/core/util/ApiMessages.properties index b58583ff..1a5eb389 100644 --- a/core/src/main/resources/net/cactusthorn/config/core/util/ApiMessages.properties +++ b/core/src/main/resources/net/cactusthorn/config/core/util/ApiMessages.properties @@ -10,4 +10,6 @@ DURATION_NO_NUMBER=No number in duration value '{0}' DURATION_WRONG_TIME_UNIT=Could not parse time unit '{0}' (try ns, us, ms, s, m, h, d) PERIOD_NO_NUMBER=No number in duration value '{0}' PERIOD_WRONG_TIME_UNIT=Could not parse time unit '{0}' (try d, w, mo, y) +MANIFEST_NOT_FOUND_1=Manifest for ''{0}={1}'' NOT found +MANIFEST_NOT_FOUND_2="Manifest for ''{0}'' NOT found." diff --git a/core/src/test/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoaderTest.java b/core/src/test/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoaderTest.java new file mode 100644 index 00000000..c39ea4e0 --- /dev/null +++ b/core/src/test/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoaderTest.java @@ -0,0 +1,54 @@ +package net.cactusthorn.config.core.loader; + +import static org.junit.jupiter.api.Assertions.*; + +import java.net.URI; +import java.util.Map; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ClasspathJarManifestLoaderTest { + + private static final Loader LOADER = new ClasspathJarManifestLoader(); + private static final ClassLoader CL = ClasspathJarManifestLoaderTest.class.getClassLoader(); + + @BeforeAll static void setUpLogger() { + Logger rootLogger = LogManager.getLogManager().getLogger(""); + rootLogger.setLevel(Level.FINE); + // switch off default Handlers to do not get anything in console + for (Handler h : rootLogger.getHandlers()) { + h.setLevel(Level.OFF); + } + } + + @Test public void accept() { + assertTrue(LOADER.accept(URI.create("classpath:jar:manifest?a=b"))); + } + + @Test public void notAccept() { + assertFalse(LOADER.accept(URI.create("classpath:a.xml#ISO-8859-1"))); + } + + @Test public void notFoundNameValue() { + LOADER.load(URI.create("classpath:jar:manifest?a=b"), CL); + } + + @Test public void notFoundName() { + LOADER.load(URI.create("classpath:jar:manifest?a"), CL); + } + + @Test public void load() { + Map result = LOADER.load(URI.create("classpath:jar:manifest?Bundle-Name=JUnit%20Jupiter%20API"), CL); + assertEquals("junit-jupiter-api", result.get("Implementation-Title")); + } + + @Test public void loadOnlyName() { + Map result = LOADER.load(URI.create("classpath:jar:manifest?Bundle-Name"), CL); + assertFalse(result.isEmpty()); + } +} diff --git a/core/src/test/java/net/cactusthorn/config/core/loader/ConfigHolderTest.java b/core/src/test/java/net/cactusthorn/config/core/loader/ConfigHolderTest.java index 34e076fa..0590679c 100644 --- a/core/src/test/java/net/cactusthorn/config/core/loader/ConfigHolderTest.java +++ b/core/src/test/java/net/cactusthorn/config/core/loader/ConfigHolderTest.java @@ -117,4 +117,8 @@ public class ConfigHolderTest { Optional> list = holder.getOptionalList(UUID::fromString, "list", ","); assertEquals(2, list.get().size()); } + + @Test public void getNotExist() { + assertThrows(IllegalArgumentException.class, () -> holder.get(UUID::fromString, "notExist")); + } } From 641c6e477514d9496fa55e72945766ac2ec3bdd0 Mon Sep 17 00:00:00 2001 From: Gmugra Date: Mon, 10 May 2021 17:33:52 +0200 Subject: [PATCH 2/3] fix muse-dev warning --- .../config/core/ConfigBuilder.java | 50 ------------------- .../loader/ClasspathJarManifestLoader.java | 12 ++--- 2 files changed, 4 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/net/cactusthorn/config/core/ConfigBuilder.java b/core/src/main/java/net/cactusthorn/config/core/ConfigBuilder.java index 04d941e3..973ee1bd 100644 --- a/core/src/main/java/net/cactusthorn/config/core/ConfigBuilder.java +++ b/core/src/main/java/net/cactusthorn/config/core/ConfigBuilder.java @@ -27,54 +27,4 @@ protected ConfigBuilder(Loaders loaders) { protected Loaders loaders() { return loaders; } - - /* - protected T get(Function convert, String key) { - return configHolder.get(convert, key); - } - - protected T get(Function convert, String key, String defaultValue) { - return configHolder.get(convert, key, defaultValue); - } - - protected Optional getOptional(Function convert, String key) { - return configHolder.getOptional(convert, key); - } - - protected List getList(Function convert, String key, String splitRegEx) { - return configHolder.getList(convert, key, splitRegEx); - } - - protected List getList(Function convert, String key, String splitRegEx, String defaultValue) { - return configHolder.getList(convert, key, splitRegEx, defaultValue); - } - - protected Optional> getOptionalList(Function convert, String key, String splitRegEx) { - return configHolder.getOptionalList(convert, key, splitRegEx); - } - - protected Set getSet(Function convert, String key, String splitRegEx) { - return configHolder.getSet(convert, key, splitRegEx); - } - - protected Set getSet(Function convert, String key, String splitRegEx, String defaultValue) { - return configHolder.getSet(convert, key, splitRegEx, defaultValue); - } - - protected Optional> getOptionalSet(Function convert, String key, String splitRegEx) { - return configHolder.getOptionalSet(convert, key, splitRegEx); - } - - protected SortedSet getSortedSet(Function convert, String key, String splitRegEx) { - return configHolder.getSortedSet(convert, key, splitRegEx); - } - - protected SortedSet getSortedSet(Function convert, String key, String splitRegEx, String defaultValue) { - return configHolder.getSortedSet(convert, key, splitRegEx, defaultValue); - } - - protected Optional> getOptionalSortedSet(Function convert, String key, String splitRegEx) { - return configHolder.getOptionalSortedSet(convert, key, splitRegEx); - } - */ } diff --git a/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java b/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java index 5641c825..40ad55a7 100644 --- a/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java +++ b/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java @@ -13,9 +13,9 @@ import java.net.URL; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; +import java.util.stream.Collectors; public class ClasspathJarManifestLoader implements Loader { @@ -31,7 +31,7 @@ public class ClasspathJarManifestLoader implements Loader { @Override public Map load(URI uri, ClassLoader classLoader) { String param = uri.getSchemeSpecificPart().substring(SUB_PREFIX.length()); - String[] parts = param.split("="); + String[] parts = param.split("=", -1); String name = parts[0]; String value = parts.length > 1 ? parts[1] : null; @@ -45,12 +45,8 @@ public class ClasspathJarManifestLoader implements Loader { Attributes attributes = manifest.getMainAttributes(); String attribute = attributes.getValue(name); if (attribute != null && (value == null || value.equals(attribute))) { - Map result = new HashMap<>(); - for (Map.Entry entry : attributes.entrySet()) { - result.put(entry.getKey().toString(), entry.getValue().toString()); - } - - return result; + return attributes.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString())); } } } From 02b7399da6924f91ce1903b6c59a5c372206ba93 Mon Sep 17 00:00:00 2001 From: Gmugra Date: Mon, 10 May 2021 17:44:05 +0200 Subject: [PATCH 3/3] fix muse-dev warning --- .../config/core/loader/ClasspathJarManifestLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java b/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java index 40ad55a7..ad29f5e6 100644 --- a/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java +++ b/core/src/main/java/net/cactusthorn/config/core/loader/ClasspathJarManifestLoader.java @@ -31,7 +31,7 @@ public class ClasspathJarManifestLoader implements Loader { @Override public Map load(URI uri, ClassLoader classLoader) { String param = uri.getSchemeSpecificPart().substring(SUB_PREFIX.length()); - String[] parts = param.split("=", -1); + String[] parts = param.split("=", 2); String name = parts[0]; String value = parts.length > 1 ? parts[1] : null;