├── .gitignore ├── .maven.xml ├── .travis.yml ├── LICENSE ├── README.md ├── config-events ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── event │ │ ├── ChangeEvent.java │ │ ├── ChangeEventNotifier.java │ │ ├── KeyFilter.java │ │ ├── SourceFilter.java │ │ ├── Type.java │ │ ├── TypeFilter.java │ │ └── regex │ │ ├── Field.java │ │ ├── RegexFilter.java │ │ └── RegexFilterInterceptor.java │ └── resources │ └── META-INF │ └── beans.xml ├── config-example ├── README.md ├── nb-configuration.xml ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── example │ │ ├── ApplicationConfig.java │ │ ├── EventChangeListener.java │ │ └── ExampleService.java │ ├── openliberty │ └── config │ │ └── server.xml │ ├── resources │ ├── examples │ │ ├── config.json │ │ ├── config.properties │ │ ├── config.xml │ │ ├── config.yaml │ │ └── config_prod.yaml │ ├── project-defaults.yml │ └── project-dev.yml │ └── webapp │ └── WEB-INF │ └── beans.xml ├── configconverter-json ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── converter │ │ └── json │ │ ├── JsonArrayConverter.java │ │ └── JsonObjectConverter.java │ └── resources │ └── META-INF │ └── services │ └── org.eclipse.microprofile.config.spi.Converter ├── configconverter-list ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── converter │ │ └── list │ │ ├── ArrayConverter.java │ │ └── ListConverter.java │ └── resources │ └── META-INF │ └── services │ └── org.eclipse.microprofile.config.spi.Converter ├── configsource-base ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── microprofileext │ └── config │ └── source │ └── base │ ├── BaseConfigSource.java │ ├── EnabledConfigSource.java │ └── ExpiringMap.java ├── configsource-consul ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── microprofileext │ │ │ └── config │ │ │ └── source │ │ │ └── consul │ │ │ ├── Configuration.java │ │ │ ├── ConsulClientWrapper.java │ │ │ └── ConsulConfigSource.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.eclipse.microprofile.config.spi.ConfigSource │ └── test │ └── java │ └── org │ └── microprofileext │ └── config │ └── source │ └── consul │ ├── ConfigurationTest.java │ ├── ConsulClientWrapperTest.java │ └── ConsulConfigSourceTest.java ├── configsource-db ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── microprofileext │ │ │ └── config │ │ │ └── source │ │ │ └── db │ │ │ ├── DatasourceConfigSource.java │ │ │ └── Repository.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.eclipse.microprofile.config.spi.ConfigSource │ └── test │ └── java │ └── org │ └── microprofileext │ └── config │ └── source │ └── db │ ├── DatasourceConfigSourceTest.java │ └── RepositoryTest.java ├── configsource-etcd ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── source │ │ └── etcd │ │ └── EtcdConfigSource.java │ └── resources │ └── META-INF │ └── services │ └── org.eclipse.microprofile.config.spi.ConfigSource ├── configsource-filebase ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── microprofileext │ └── config │ └── source │ └── base │ └── file │ ├── AbstractUrlBasedSource.java │ ├── FileResourceWatcher.java │ ├── Reloadable.java │ └── WebResourceWatcher.java ├── configsource-json ├── README.md ├── application.json ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── microprofileext │ │ │ └── config │ │ │ └── source │ │ │ └── json │ │ │ └── JsonConfigSource.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.eclipse.microprofile.config.spi.ConfigSource │ └── test │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── source │ │ └── json │ │ ├── DisabledWhenEnabledKeyIsFalseTest.java │ │ ├── EnabledWhenEnabledKeyIsMissingTest.java │ │ ├── EnabledWhenEnabledKeyIsTrueTest.java │ │ └── ListTest.java │ └── resources │ ├── META-INF │ └── beans.xml │ ├── config-disabled.properties │ ├── config-empty.properties │ └── config-enabled.properties ├── configsource-memory ├── README.md ├── pom.xml ├── screenshot.png └── src │ └── main │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── source │ │ └── memory │ │ ├── MemoryConfigApi.java │ │ └── MemoryConfigSource.java │ └── resources │ └── META-INF │ ├── beans.xml │ └── services │ └── org.eclipse.microprofile.config.spi.ConfigSource ├── configsource-properties ├── README.md ├── application.properties ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── microprofileext │ │ │ └── config │ │ │ └── source │ │ │ └── properties │ │ │ └── PropertiesConfigSource.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.eclipse.microprofile.config.spi.ConfigSource │ └── test │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── source │ │ └── properties │ │ ├── DisabledWhenEnabledKeyIsFalseTest.java │ │ ├── EnabledWhenEnabledKeyIsMissingTest.java │ │ ├── EnabledWhenEnabledKeyIsTrueTest.java │ │ └── ListTest.java │ └── resources │ ├── META-INF │ └── beans.xml │ ├── config-disabled.properties │ ├── config-empty.properties │ └── config-enabled.properties ├── configsource-providers ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── cdi │ │ ├── ConfigSourceMap.java │ │ ├── ConfigSourceMapProvider.java │ │ ├── ConfigSourceProvider.java │ │ └── Name.java │ └── resources │ └── META-INF │ └── beans.xml ├── configsource-typesafeconfig ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── microprofileext │ │ │ └── config │ │ │ └── source │ │ │ └── typesafeconfig │ │ │ └── TypeSafeConfigConfigSource.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.eclipse.microprofile.config.spi.ConfigSource │ └── test │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── source │ │ └── typesafeconfig │ │ └── TypeSafeConfigConfigSourceTest.java │ └── resources │ └── TestConfig.conf ├── configsource-xml ├── README.md ├── application.xml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── microprofileext │ │ │ └── config │ │ │ └── source │ │ │ └── xml │ │ │ └── XmlConfigSource.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.eclipse.microprofile.config.spi.ConfigSource │ └── test │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── source │ │ └── xml │ │ ├── DisabledWhenEnabledKeyIsFalseTest.java │ │ ├── EnabledWhenEnabledKeyIsMissingTest.java │ │ ├── EnabledWhenEnabledKeyIsTrueTest.java │ │ └── ListTest.java │ └── resources │ ├── META-INF │ └── beans.xml │ ├── config-disabled.properties │ ├── config-empty.properties │ └── config-enabled.properties ├── configsource-yaml ├── README.md ├── application.yaml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── microprofileext │ │ │ └── config │ │ │ └── source │ │ │ └── yaml │ │ │ └── YamlConfigSource.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.eclipse.microprofile.config.spi.ConfigSource │ └── test │ ├── java │ └── org │ │ └── microprofileext │ │ └── config │ │ └── source │ │ └── yaml │ │ ├── DisabledWhenEnabledKeyIsFalseTest.java │ │ ├── EnabledWhenEnabledKeyIsMissingTest.java │ │ ├── EnabledWhenEnabledKeyIsTrueTest.java │ │ └── ListTest.java │ └── resources │ ├── META-INF │ └── beans.xml │ ├── config-disabled.properties │ ├── config-empty.properties │ └── config-enabled.properties ├── nbactions.xml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # IntelliJ 26 | .idea/ 27 | *.iml 28 | 29 | # eclipse 30 | .classpath 31 | .project 32 | .settings/ 33 | target/ 34 | */pom.xml.releaseBackup 35 | */pom.xml.versionsBackup 36 | pom.xml.releaseBackup 37 | pom.xml.versionsBackup 38 | -------------------------------------------------------------------------------- /.maven.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | ossrh 7 | ${env.SONATYPE_USERNAME} 8 | ${env.SONATYPE_PASSWORD} 9 | 10 | 11 | 12 | 13 | 14 | ossrh 15 | 16 | true 17 | 18 | 19 | ${env.GPG_EXECUTABLE} 20 | ${env.GPG_PASSPHRASE} 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | before_deploy: 4 | - mvn help:evaluate -N -Dexpression=project.version|grep -v '\[' 5 | - export project_version=$(mvn help:evaluate -N -Dexpression=project.version|grep -v '\[') 6 | 7 | ## export GPG details 8 | before_install: 9 | - export GPG_TTY=$(tty) 10 | - echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import --no-tty --batch 11 | - echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust 12 | 13 | install: 14 | mvn --settings .maven.xml install -DskipTests=true -Dgpg.skip -Dmaven.javadoc.skip=true -B -V 15 | 16 | ## Build and release to maven central 17 | script: 18 | mvn clean deploy --settings .maven.xml -DskipTests=true -B -U -Prelease 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [Extensions for MicroProfile](https://www.microprofile-ext.org/) 3 | 4 | ## Config Extensions 5 | 6 | [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://github.com/microprofile-extensions/config-ext/blob/master/LICENSE) 7 | ___________ 8 | Here you will find some extra Config sources, Config converters and some utils for MicroProfile Config API. 9 | 10 | ### Config Sources 11 | * [Memory Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-memory/README.md) 12 | * [Properties Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-properties/README.md) 13 | * [Yaml Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-yaml/README.md) 14 | * [Json Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-json/README.md) 15 | * [Xml Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-xml/README.md) 16 | * [Etcd Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-etcd/README.md) 17 | * [DB Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-db/README.md) 18 | * [Consul Config source](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-consul/README.md) 19 | * [TypeSafe Config source](https://github.com/microprofile-extensions/config-ext/tree/master/configsource-typesafeconfig/README.md) 20 | 21 | ### Config utils 22 | * [Config events](https://github.com/microprofile-extensions/config-ext/blob/master/config-events/README.md) 23 | * [Config source CDI Providers](https://github.com/microprofile-extensions/config-ext/blob/master/configsource-providers/README.md) 24 | 25 | ### Config Converters 26 | * [List Config converter](https://github.com/microprofile-extensions/config-ext/blob/master/configconverter-list/README.md) 27 | * [Json Config converter](https://github.com/microprofile-extensions/config-ext/blob/master/configconverter-json/README.md) 28 | 29 | ### Example 30 | 31 | Also look at the [example application](https://github.com/microprofile-extensions/config-ext/blob/master/config-example/README.md) to see how this is used 32 | -------------------------------------------------------------------------------- /config-events/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | config-events 12 | 13 | Microprofile Config Extensions :: Config events 14 | A library to make it easy to add config events 15 | 16 | -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/ChangeEvent.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event; 2 | 3 | import java.io.Serializable; 4 | import java.util.Objects; 5 | import java.util.Optional; 6 | 7 | /** 8 | * an event on config element 9 | * @author Phillip Kruger 10 | */ 11 | public class ChangeEvent implements Serializable { 12 | 13 | private Type type; 14 | private String key; 15 | private Optional oldValue; 16 | private String newValue; 17 | private String fromSource; 18 | 19 | public ChangeEvent(Type type, String key, Optional oldValue, String newValue, String fromSource) { 20 | this.type = type; 21 | this.key = key; 22 | this.oldValue = oldValue; 23 | this.newValue = newValue; 24 | this.fromSource = fromSource; 25 | } 26 | 27 | public Type getType() { 28 | return type; 29 | } 30 | 31 | public void setType(Type type) { 32 | this.type = type; 33 | } 34 | 35 | public String getKey() { 36 | return key; 37 | } 38 | 39 | public void setKey(String key) { 40 | this.key = key; 41 | } 42 | 43 | public Optional getOldValue() { 44 | return oldValue; 45 | } 46 | 47 | public void setOldValue(Optional oldValue) { 48 | this.oldValue = oldValue; 49 | } 50 | 51 | public String getNewValue() { 52 | return newValue; 53 | } 54 | 55 | public void setNewValue(String newValue) { 56 | this.newValue = newValue; 57 | } 58 | 59 | public String getFromSource() { 60 | return fromSource; 61 | } 62 | 63 | public void setFromSource(String fromSource) { 64 | this.fromSource = fromSource; 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | int hash = 3; 70 | hash = 79 * hash + Objects.hashCode(this.type); 71 | hash = 79 * hash + Objects.hashCode(this.key); 72 | hash = 79 * hash + Objects.hashCode(this.oldValue); 73 | hash = 79 * hash + Objects.hashCode(this.newValue); 74 | hash = 79 * hash + Objects.hashCode(this.fromSource); 75 | return hash; 76 | } 77 | 78 | @Override 79 | public boolean equals(Object obj) { 80 | if (this == obj) { 81 | return true; 82 | } 83 | if (obj == null) { 84 | return false; 85 | } 86 | if (getClass() != obj.getClass()) { 87 | return false; 88 | } 89 | final ChangeEvent other = (ChangeEvent) obj; 90 | if (!Objects.equals(this.key, other.key)) { 91 | return false; 92 | } 93 | if (!Objects.equals(this.newValue, other.newValue)) { 94 | return false; 95 | } 96 | if (!Objects.equals(this.fromSource, other.fromSource)) { 97 | return false; 98 | } 99 | if (this.type != other.type) { 100 | return false; 101 | } 102 | if (!Objects.equals(this.oldValue, other.oldValue)) { 103 | return false; 104 | } 105 | return true; 106 | } 107 | 108 | 109 | } 110 | -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/ChangeEventNotifier.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Optional; 8 | import java.util.Set; 9 | import jakarta.enterprise.context.ApplicationScoped; 10 | import jakarta.enterprise.context.Initialized; 11 | import jakarta.enterprise.event.Event; 12 | import jakarta.enterprise.event.Observes; 13 | import jakarta.inject.Inject; 14 | 15 | /** 16 | * Easy way to fire a change event 17 | * @author Phillip Kruger 18 | * 19 | * This gets used from Config sources that is not in the CDI Context. So we can not @Inject a bean. 20 | * For some reason, CDI.current() is only working on Payara, and not on Thorntail and OpenLiberty, so this ugly footwork is to 21 | * get around that. 22 | */ 23 | @ApplicationScoped 24 | public class ChangeEventNotifier { 25 | 26 | @Inject 27 | private Event broadcaster; 28 | 29 | private static ChangeEventNotifier INSTANCE; 30 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 31 | INSTANCE = this; 32 | } 33 | 34 | public static ChangeEventNotifier getInstance(){ 35 | //return CDI.current().select(ChangeEventNotifier.class).get(); 36 | return INSTANCE; 37 | } 38 | 39 | public void detectChangesAndFire(Map before,Map after, String fromSource){ 40 | List changes = new ArrayList<>(); 41 | if(!before.equals(after)){ 42 | Set> beforeEntries = before.entrySet(); 43 | for(Map.Entry beforeEntry:beforeEntries){ 44 | String key = beforeEntry.getKey(); 45 | String oldValue = beforeEntry.getValue(); 46 | if(after.containsKey(key)){ 47 | String newValue = after.get(key); 48 | if(!newValue.equals(oldValue)){ 49 | // Update 50 | changes.add(new ChangeEvent(Type.UPDATE, key, getOptionalOldValue(oldValue), newValue, fromSource)); 51 | } 52 | after.remove(key); 53 | }else{ 54 | // Removed. 55 | changes.add(new ChangeEvent(Type.REMOVE, key, getOptionalOldValue(oldValue), null, fromSource)); 56 | } 57 | } 58 | 59 | Set> newEntries = after.entrySet(); 60 | for(Map.Entry newEntry:newEntries){ 61 | // New 62 | changes.add(new ChangeEvent(Type.NEW, newEntry.getKey(), Optional.empty(), newEntry.getValue(), fromSource)); 63 | } 64 | } 65 | 66 | if(!changes.isEmpty())fire(changes); 67 | } 68 | 69 | public void fire(ChangeEvent changeEvent){ 70 | List annotationList = new ArrayList<>(); 71 | annotationList.add(new TypeFilter.TypeFilterLiteral(changeEvent.getType())); 72 | annotationList.add(new KeyFilter.KeyFilterLiteral(changeEvent.getKey())); 73 | annotationList.add(new SourceFilter.SourceFilterLiteral(changeEvent.getFromSource())); 74 | 75 | broadcaster.select(annotationList.toArray(new Annotation[annotationList.size()])).fire(changeEvent); 76 | } 77 | 78 | public void fire(List changeEvents){ 79 | for(ChangeEvent changeEvent : changeEvents){ 80 | fire(changeEvent); 81 | } 82 | } 83 | 84 | public Optional getOptionalOldValue(String oldValue){ 85 | if(oldValue==null || oldValue.isEmpty())return Optional.empty(); 86 | return Optional.of(oldValue); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/KeyFilter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Target; 5 | import jakarta.inject.Qualifier; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import jakarta.enterprise.util.AnnotationLiteral; 10 | 11 | /** 12 | * Filter the event on the key 13 | * @author Phillip Kruger 14 | */ 15 | @Qualifier 16 | @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Documented 19 | public @interface KeyFilter { 20 | String value(); 21 | 22 | class KeyFilterLiteral extends AnnotationLiteral implements KeyFilter { 23 | private String key; 24 | 25 | public KeyFilterLiteral(){ 26 | 27 | } 28 | 29 | public KeyFilterLiteral(String key) { 30 | this.key = key; 31 | } 32 | 33 | @Override 34 | public String value(){ 35 | return this.key; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/SourceFilter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Target; 5 | import jakarta.inject.Qualifier; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import jakarta.enterprise.util.AnnotationLiteral; 10 | 11 | /** 12 | * Filter by config source 13 | * @author Phillip Kruger 14 | */ 15 | @Qualifier 16 | @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Documented 19 | public @interface SourceFilter { 20 | String value(); 21 | 22 | class SourceFilterLiteral extends AnnotationLiteral implements SourceFilter { 23 | private String name; 24 | 25 | public SourceFilterLiteral() { 26 | } 27 | 28 | public SourceFilterLiteral(String name) { 29 | this.name = name; 30 | } 31 | 32 | @Override 33 | public String value(){ 34 | return this.name; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/Type.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event; 2 | 3 | public enum Type { 4 | NEW,REMOVE,UPDATE 5 | } -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/TypeFilter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Target; 5 | import jakarta.inject.Qualifier; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import jakarta.enterprise.util.AnnotationLiteral; 10 | 11 | /** 12 | * filter by change type 13 | * @author Phillip Kruger 14 | */ 15 | @Qualifier 16 | @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Documented 19 | public @interface TypeFilter { 20 | Type value(); 21 | 22 | class TypeFilterLiteral extends AnnotationLiteral implements TypeFilter { 23 | private Type type; 24 | 25 | public TypeFilterLiteral() { 26 | } 27 | 28 | public TypeFilterLiteral(Type type) { 29 | this.type = type; 30 | } 31 | 32 | @Override 33 | public Type value(){ 34 | return this.type; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/regex/Field.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event.regex; 2 | 3 | /** 4 | * a field to apply a regex on 5 | * @author Phillip Kruger 6 | */ 7 | public enum Field { 8 | key,oldValue,newValue,fromSource 9 | } 10 | -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/regex/RegexFilter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event.regex; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | import jakarta.enterprise.util.Nonbinding; 9 | import jakarta.interceptor.InterceptorBinding; 10 | 11 | /** 12 | * an interceptor that match the value to a regular expression 13 | * @author Phillip Kruger 14 | */ 15 | @Inherited 16 | @InterceptorBinding 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Target({ElementType.METHOD, ElementType.TYPE}) 19 | public @interface RegexFilter { 20 | @Nonbinding String value(); 21 | @Nonbinding Field onField() default Field.key; 22 | } 23 | -------------------------------------------------------------------------------- /config-events/src/main/java/org/microprofileext/config/event/regex/RegexFilterInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.event.regex; 2 | 3 | import java.util.Optional; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | import jakarta.annotation.Priority; 9 | import jakarta.interceptor.AroundInvoke; 10 | import jakarta.interceptor.Interceptor; 11 | import jakarta.interceptor.InvocationContext; 12 | import org.microprofileext.config.event.ChangeEvent; 13 | 14 | @RegexFilter(value = "") 15 | @Interceptor 16 | @Priority(100) 17 | public class RegexFilterInterceptor { 18 | 19 | private static final Logger log = Logger.getLogger(RegexFilterInterceptor.class.getName()); 20 | 21 | @AroundInvoke 22 | public Object observer(InvocationContext ctx) throws Exception { 23 | 24 | RegexFilter regexFilterAnnotation = ctx.getMethod().getAnnotation(RegexFilter.class); 25 | Field onField = regexFilterAnnotation.onField(); 26 | String regex = regexFilterAnnotation.value(); 27 | 28 | Optional posibleChangeEvent = getChangeEvent(ctx); 29 | 30 | if(posibleChangeEvent.isPresent()){ 31 | ChangeEvent changeEvent = posibleChangeEvent.get(); 32 | String value = getValueToApplyRegexOn(changeEvent,onField); 33 | Pattern pattern = Pattern.compile(regex); 34 | Matcher matcher = pattern.matcher(value); 35 | boolean b = matcher.matches(); 36 | if(!b)return null; 37 | }else{ 38 | log.log(Level.WARNING, "Can not find ChangeEvent parameter for method {0}. @RegexFilter is being ignored", ctx.getMethod().getName()); 39 | } 40 | return ctx.proceed(); 41 | } 42 | 43 | private String getValueToApplyRegexOn(ChangeEvent changeEvent,Field onField){ 44 | String value = null; 45 | switch (onField){ 46 | case key: 47 | value = changeEvent.getKey(); 48 | break; 49 | case fromSource: 50 | value = changeEvent.getFromSource(); 51 | break; 52 | case newValue: 53 | value = changeEvent.getNewValue(); 54 | break; 55 | case oldValue: 56 | value = changeEvent.getOldValue().orElse(""); 57 | } 58 | 59 | return value; 60 | } 61 | 62 | private Optional getChangeEvent(InvocationContext ctx){ 63 | Object[] parameters = ctx.getParameters(); 64 | 65 | for(Object parameter:parameters){ 66 | if(parameter.getClass().equals(ChangeEvent.class)){ 67 | ChangeEvent changeEvent = (ChangeEvent)parameter; 68 | return Optional.of(changeEvent); 69 | } 70 | } 71 | return Optional.empty(); 72 | } 73 | } -------------------------------------------------------------------------------- /config-events/src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /config-example/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Example Application 4 | 5 | This is a very basic example application to demonstrate the Config extensions. 6 | 7 | ## Running the example. 8 | 9 | Using maven, you can start this application using Thorntail, Payara or OpenLiberty: 10 | 11 | 12 | ``` 13 | mvn -Pthorntail clean install 14 | ``` 15 | or 16 | ``` 17 | mvn -Ppayara clean install 18 | ``` 19 | or 20 | ``` 21 | mvn -Popenliberty clean install 22 | ``` 23 | 24 | You can then go to http://localhost:8080/config-example/api/openapi-ui 25 | -------------------------------------------------------------------------------- /config-example/nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | ide 17 | 18 | 19 | -------------------------------------------------------------------------------- /config-example/src/main/java/org/microprofileext/config/example/ApplicationConfig.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.example; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; 6 | import org.eclipse.microprofile.openapi.annotations.info.Contact; 7 | import org.eclipse.microprofile.openapi.annotations.info.Info; 8 | import org.eclipse.microprofile.openapi.annotations.servers.Server; 9 | 10 | /** 11 | * Activate JAX-RS. 12 | * All REST Endpoints available under /api 13 | * 14 | * @author Phillip Kruger 15 | */ 16 | @ApplicationPath("/api") 17 | @OpenAPIDefinition(info = @Info( 18 | title = "Example service", 19 | version = "1.0.0", 20 | contact = @Contact( 21 | name = "Phillip Kruger", 22 | email = "phillip.kruger@phillip-kruger.com", 23 | url = "http://www.phillip-kruger.com") 24 | ), 25 | servers = { 26 | @Server(url = "/config-example",description = "localhost") 27 | } 28 | ) 29 | public class ApplicationConfig extends Application { 30 | 31 | } 32 | -------------------------------------------------------------------------------- /config-example/src/main/java/org/microprofileext/config/example/EventChangeListener.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.example; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | import jakarta.enterprise.context.ApplicationScoped; 6 | import jakarta.enterprise.event.Observes; 7 | import jakarta.enterprise.inject.spi.EventMetadata; 8 | import org.microprofileext.config.event.ChangeEvent; 9 | import org.microprofileext.config.event.Type; 10 | import org.microprofileext.config.event.KeyFilter; 11 | import org.microprofileext.config.event.regex.RegexFilter; 12 | import org.microprofileext.config.event.SourceFilter; 13 | import org.microprofileext.config.event.TypeFilter; 14 | import org.microprofileext.config.event.regex.Field; 15 | 16 | /** 17 | * Example Listener for changes in the events 18 | * @author Phillip Kruger 19 | */ 20 | @ApplicationScoped 21 | public class EventChangeListener { 22 | 23 | private static final Logger log = Logger.getLogger(EventChangeListener.class.getName()); 24 | 25 | // Getting all config event 26 | public void all(@Observes ChangeEvent changeEvent){ 27 | log.log(Level.SEVERE, "ALL: Received a config change event: {0}", changeEvent); 28 | } 29 | 30 | // Get only new values 31 | public void newValue(@Observes @TypeFilter(Type.NEW) ChangeEvent changeEvent){ 32 | log.log(Level.SEVERE, "NEW: Received a config change event: {0}", changeEvent); 33 | } 34 | 35 | // Get only override values 36 | public void overrideValue(@Observes @TypeFilter(Type.UPDATE) ChangeEvent changeEvent){ 37 | log.log(Level.SEVERE, "UPDATE: Received a config change event: {0}", changeEvent); 38 | } 39 | 40 | // Get only revert values 41 | public void revertValue(@Observes @TypeFilter(Type.REMOVE) ChangeEvent changeEvent){ 42 | log.log(Level.SEVERE, "REMOVE: Received a config change event: {0}", changeEvent); 43 | } 44 | 45 | // Getting all config event when key is some.key 46 | public void allForKey(@Observes @KeyFilter("some.key") ChangeEvent changeEvent){ 47 | log.log(Level.SEVERE, "ALL for key [some.key]: Received a config change event: {0}", changeEvent); 48 | } 49 | 50 | @RegexFilter("^some\\..+") // Starting with some. 51 | public void allForPatternMatchOnKey(@Observes ChangeEvent changeEvent, EventMetadata meta){ 52 | log.log(Level.SEVERE, "Pattern match on key: Received a config change event: {0}", changeEvent); 53 | } 54 | 55 | @RegexFilter(onField = Field.oldValue, value = "^some\\..+") // Starting with some. 56 | public void allForPatternMatchOnOldValue(@Observes ChangeEvent changeEvent, EventMetadata meta){ 57 | log.log(Level.SEVERE, "Pattern match on old value: Received a config change event: {0}", changeEvent); 58 | } 59 | 60 | // Getting all config event when key is some.key for new events 61 | public void newForKey(@Observes @TypeFilter(Type.NEW) @KeyFilter("some.key") ChangeEvent changeEvent){ 62 | log.log(Level.SEVERE, "NEW for key [some.key]: Received a config change event: {0}", changeEvent); 63 | } 64 | 65 | // Getting all config event when key is some.key for override events 66 | public void overrideForKey(@Observes @TypeFilter(Type.UPDATE) @KeyFilter("some.key") ChangeEvent changeEvent){ 67 | log.log(Level.SEVERE, "UPDATE for key [some.key]: Received a config change event: {0}", changeEvent); 68 | } 69 | 70 | // Getting all config event when key is some.key for revert events 71 | public void revertForKey(@Observes @TypeFilter(Type.REMOVE) @KeyFilter("some.key") ChangeEvent changeEvent){ 72 | log.log(Level.SEVERE, "REMOVE for key [some.key]: Received a config change event: {0}", changeEvent); 73 | } 74 | 75 | // Getting all config events for a certain source 76 | public void allForSource(@Observes @SourceFilter("MemoryConfigSource") ChangeEvent changeEvent){ 77 | log.log(Level.SEVERE, "ALL for source [MemoryConfigSource]: Received a config change event: {0}", changeEvent); 78 | } 79 | 80 | // Getting all config events for a certain source 81 | public void allForSourceAndKey(@Observes @SourceFilter("MemoryConfigSource") @KeyFilter("some.key") ChangeEvent changeEvent){ 82 | log.log(Level.SEVERE, "ALL for source [MemoryConfigSource] and for key [some.key]: Received a config change event: {0}", changeEvent); 83 | } 84 | 85 | // Getting all config events for a certain source 86 | public void overrideForSourceAndKey(@Observes @TypeFilter(Type.UPDATE) @SourceFilter("MemoryConfigSource") @KeyFilter("some.key") ChangeEvent changeEvent){ 87 | log.log(Level.SEVERE, "UPDATE for source [MemoryConfigSource] and for key [some.key]: Received a config change event: {0}", changeEvent); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /config-example/src/main/java/org/microprofileext/config/example/ExampleService.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.example; 2 | 3 | import java.util.logging.Logger; 4 | import jakarta.enterprise.context.RequestScoped; 5 | import jakarta.inject.Inject; 6 | import jakarta.inject.Provider; 7 | import jakarta.json.Json; 8 | import jakarta.json.JsonArrayBuilder; 9 | import jakarta.ws.rs.Consumes; 10 | import jakarta.ws.rs.GET; 11 | import jakarta.ws.rs.Path; 12 | import jakarta.ws.rs.PathParam; 13 | import jakarta.ws.rs.Produces; 14 | import jakarta.ws.rs.core.MediaType; 15 | import jakarta.ws.rs.core.Response; 16 | import org.eclipse.microprofile.config.Config; 17 | import org.eclipse.microprofile.config.inject.ConfigProperty; 18 | import org.eclipse.microprofile.config.spi.ConfigSource; 19 | import org.eclipse.microprofile.openapi.annotations.Operation; 20 | import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; 21 | import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; 22 | import org.eclipse.microprofile.openapi.annotations.tags.Tag; 23 | 24 | /** 25 | * Example Service. JAX-RS 26 | * @author Phillip Kruger 27 | */ 28 | @RequestScoped 29 | @Path("/") 30 | @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) 31 | @Tag(name = "Example service",description = "Just some example") 32 | public class ExampleService { 33 | 34 | private static final Logger log = Logger.getLogger(ExampleService.class.getName()); 35 | 36 | @Inject 37 | private Config config; 38 | 39 | @Inject @ConfigProperty(name="ysomekey") 40 | private Provider ysomekey; 41 | 42 | @GET 43 | @Path("/all") 44 | @Produces(MediaType.APPLICATION_JSON) 45 | @Operation(operationId = "all", description = "Getting all config keys and values") 46 | @APIResponse(responseCode = "200", description = "Successful, returning the key-value in JSON format") 47 | public Response getAll(){ 48 | JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); 49 | for(String property:config.getPropertyNames()){ 50 | String value = config.getValue(property, String.class); 51 | arrayBuilder.add(Json.createObjectBuilder().add(property, value).build()); 52 | } 53 | return Response.ok(arrayBuilder.build()).build(); 54 | } 55 | 56 | @GET 57 | @Path("/key/{key}") 58 | @Operation(operationId = "value", description = "Getting the value for a certain config key") 59 | @APIResponse(responseCode = "200", description = "Successful, returning the value") 60 | @Produces(MediaType.TEXT_PLAIN) 61 | public Response getValue(@Parameter(name = "key", description = "The key for this config", required = true, allowEmptyValue = false, example = "some.key") 62 | @PathParam("key") String key) { 63 | return Response.ok(config.getOptionalValue(key, String.class).orElse(null)).build(); 64 | } 65 | 66 | @GET 67 | @Path("/ysomekey") 68 | @Operation(operationId = "valueYsomevalue", description = "Getting the value for ysomekey") 69 | @APIResponse(responseCode = "200", description = "Successful, returning the value") 70 | @Produces(MediaType.TEXT_PLAIN) 71 | public Response getValueYsomevalue() { 72 | return Response.ok(ysomekey.get()).build(); 73 | } 74 | 75 | @GET 76 | @Path("/sources") 77 | @Produces(MediaType.APPLICATION_JSON) 78 | @Operation(operationId = "configSources", description = "Getting all the current config sources") 79 | @APIResponse(responseCode = "200", description = "Successful, returning the config sources in JSON format") 80 | public Response getConfigSources(){ 81 | JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); 82 | for(ConfigSource source:config.getConfigSources()){ 83 | arrayBuilder.add(Json.createObjectBuilder().add(String.valueOf(source.getOrdinal()), source.getName()).build()); 84 | } 85 | return Response.ok(arrayBuilder.build()).build(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /config-example/src/main/openliberty/config/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | microProfile-5.0 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /config-example/src/main/resources/examples/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsomekey": "somevalue", 3 | "jlocation": { 4 | "protocol": "http", 5 | "host": "localhost", 6 | "port": 8080, 7 | "path": "/some/path", 8 | "jedis": [ 9 | "Yoda", 10 | "Qui-Gon Jinn", 11 | "Obi-Wan Kenobi", 12 | "Luke Skywalker" 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /config-example/src/main/resources/examples/config.properties: -------------------------------------------------------------------------------- 1 | psomekey=somevalue 2 | plocation.protocol=http 3 | plocation.host=localhost 4 | plocation.port=8080 5 | plocation.path=/some/path 6 | plocation.jedis=[Yoda, Qui-Gon Jinn, Obi-Wan Kenobi, Luke Skywalker] -------------------------------------------------------------------------------- /config-example/src/main/resources/examples/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | somevalue 4 | 5 | http 6 | localhost 7 | 8080 8 | /some/path 9 | Yoda 10 | Qui-Gon Jinn 11 | Obi-Wan Kenobi 12 | Luke Skywalker 13 | 14 | -------------------------------------------------------------------------------- /config-example/src/main/resources/examples/config.yaml: -------------------------------------------------------------------------------- 1 | ysomekey: "somevalue" 2 | ylocation: 3 | protocol: "http" 4 | host: "localhosts" 5 | port: 8080 6 | path: "/some/path" 7 | jedis: 8 | - Yoda 9 | - Qui-Gon Jinn 10 | - Obi-Wan Kenobi 11 | - Luke Skywalker 12 | -------------------------------------------------------------------------------- /config-example/src/main/resources/examples/config_prod.yaml: -------------------------------------------------------------------------------- 1 | ylocation: 2 | host: "www.prodserver.co.za" 3 | -------------------------------------------------------------------------------- /config-example/src/main/resources/project-defaults.yml: -------------------------------------------------------------------------------- 1 | none: -------------------------------------------------------------------------------- /config-example/src/main/resources/project-dev.yml: -------------------------------------------------------------------------------- 1 | thorntail: 2 | logging: 3 | loggers: 4 | org.microprofileext.config: 5 | level: FINEST -------------------------------------------------------------------------------- /config-example/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /configconverter-json/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Json converter 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configconverter-json/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configconverter-json) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configconverter-json.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configconverter-json) 7 | 8 | This extension allows you to inject a JsonObject or JsonArray as a ConfigProperty 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configconverter-json 17 | XXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Example 24 | 25 | In the java code: 26 | 27 | ```java 28 | 29 | @Inject 30 | @ConfigProperty(name = "someJsonArray") 31 | private JsonArray someValue; 32 | 33 | @Inject 34 | @ConfigProperty(name = "someJsonObject") 35 | private JsonObject someValue; 36 | 37 | ``` 38 | 39 | When using the property: 40 | 41 | ``` 42 | 43 | someJsonArray=["value1","value2","value3"] 44 | someJsonObject={"foo": "bar", "count":100} 45 | 46 | ``` 47 | -------------------------------------------------------------------------------- /configconverter-json/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configconverter-json 12 | 13 | Microprofile Config Extensions :: Json converter 14 | A Json Object and Json Array converter 15 | 16 | 17 | -------------------------------------------------------------------------------- /configconverter-json/src/main/java/org/microprofileext/config/converter/json/JsonArrayConverter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.converter.json; 2 | 3 | import java.io.StringReader; 4 | import jakarta.json.Json; 5 | import jakarta.json.JsonArray; 6 | import jakarta.json.JsonReader; 7 | import org.eclipse.microprofile.config.spi.Converter; 8 | 9 | /** 10 | * Converts a json string to a JSonArray 11 | * @author Phillip Kruger 12 | */ 13 | public class JsonArrayConverter implements Converter { 14 | 15 | @Override 16 | public JsonArray convert(String input) throws IllegalArgumentException { 17 | if(isNullOrEmpty(input))return Json.createArrayBuilder().build(); 18 | 19 | try(JsonReader jsonReader = Json.createReader(new StringReader(input))){ 20 | return jsonReader.readArray(); 21 | } 22 | } 23 | 24 | /** 25 | * Not to sure about this, got an jakarta.json.stream.JsonParsingException in Wildfly with a value of org.eclipse.microprofile.config.configproperty.unconfigureddvalue 26 | **/ 27 | private boolean isNullOrEmpty(String input){ 28 | return input==null || input.isEmpty() || input.equals(UNCONFIGURED_VALUE); 29 | } 30 | 31 | private static final String UNCONFIGURED_VALUE = "org.eclipse.microprofile.config.configproperty.unconfigureddvalue"; 32 | } 33 | -------------------------------------------------------------------------------- /configconverter-json/src/main/java/org/microprofileext/config/converter/json/JsonObjectConverter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.converter.json; 2 | 3 | import java.io.StringReader; 4 | import jakarta.json.Json; 5 | import jakarta.json.JsonObject; 6 | import jakarta.json.JsonReader; 7 | import org.eclipse.microprofile.config.spi.Converter; 8 | 9 | /** 10 | * Converts a json string to a JSonObject 11 | * @author Phillip Kruger 12 | */ 13 | public class JsonObjectConverter implements Converter { 14 | 15 | @Override 16 | public JsonObject convert(String input) throws IllegalArgumentException { 17 | if(isNullOrEmpty(input))return Json.createObjectBuilder().build(); 18 | 19 | try(JsonReader jsonReader = Json.createReader(new StringReader(input))){ 20 | return jsonReader.readObject(); 21 | } 22 | } 23 | 24 | /** 25 | * Not to sure about this, got an jakarta.json.stream.JsonParsingException in Wildfly with a value of org.eclipse.microprofile.config.configproperty.unconfigureddvalue 26 | **/ 27 | private boolean isNullOrEmpty(String input){ 28 | return input==null || input.isEmpty() || input.equals(UNCONFIGURED_VALUE); 29 | } 30 | 31 | private static final String UNCONFIGURED_VALUE = "org.eclipse.microprofile.config.configproperty.unconfigureddvalue"; 32 | } 33 | -------------------------------------------------------------------------------- /configconverter-json/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.converter.json.JsonObjectConverter 2 | org.microprofileext.config.converter.json.JsonArrayConverter -------------------------------------------------------------------------------- /configconverter-list/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # List Converter 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configconverter-list/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configconverter-list) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configconverter-list.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configconverter-list) 7 | 8 | This extension allows you to inject a List or Array as a ConfigProperty 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configconverter-list 17 | XXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Example 24 | 25 | In the java code: 26 | 27 | ```java 28 | 29 | @Inject 30 | @ConfigProperty(name = "someList") 31 | private List someValue; 32 | 33 | @Inject 34 | @ConfigProperty(name = "someArray") 35 | private String[] someValue; 36 | 37 | ``` 38 | 39 | When using the property: 40 | 41 | ``` 42 | 43 | someList=value1,value2,value3 44 | someArray=value1,value2,value3 45 | 46 | ``` 47 | -------------------------------------------------------------------------------- /configconverter-list/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configconverter-list 12 | 13 | Microprofile Config Extensions :: List converter 14 | A List and Array converter 15 | 16 | 17 | -------------------------------------------------------------------------------- /configconverter-list/src/main/java/org/microprofileext/config/converter/list/ArrayConverter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.converter.list; 2 | 3 | import java.util.stream.Collectors; 4 | import java.util.stream.Stream; 5 | import org.eclipse.microprofile.config.spi.Converter; 6 | 7 | /** 8 | * Converts a comma separated string to a String array 9 | * @author Phillip Kruger 10 | */ 11 | public class ArrayConverter implements Converter { 12 | 13 | @Override 14 | public String[] convert(String input) throws IllegalArgumentException { 15 | if(isNullOrEmpty(input))return new String[]{}; 16 | 17 | Stream stream = Stream.of(input); 18 | return stream.collect(Collectors.toList()).toArray(new String[]{}); 19 | } 20 | 21 | /** 22 | * Not to sure about this, got an jakarta.json.stream.JsonParsingException in Wildfly with a value of org.eclipse.microprofile.config.configproperty.unconfigureddvalue 23 | **/ 24 | private boolean isNullOrEmpty(String input){ 25 | return input==null || input.isEmpty() || input.equals(UNCONFIGURED_VALUE); 26 | } 27 | 28 | private static final String UNCONFIGURED_VALUE = "org.eclipse.microprofile.config.configproperty.unconfigureddvalue"; 29 | } 30 | -------------------------------------------------------------------------------- /configconverter-list/src/main/java/org/microprofileext/config/converter/list/ListConverter.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.converter.list; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.Stream; 7 | import org.eclipse.microprofile.config.spi.Converter; 8 | 9 | /** 10 | * Converts a comma separated string to a list 11 | * @author Phillip Kruger 12 | */ 13 | public class ListConverter implements Converter { 14 | 15 | @Override 16 | public List convert(String input) throws IllegalArgumentException { 17 | if(isNullOrEmpty(input))return new ArrayList(); 18 | 19 | Stream stream = Stream.of(input); 20 | return stream.collect(Collectors.toList()); 21 | } 22 | 23 | /** 24 | * Not to sure about this, got an jakarta.json.stream.JsonParsingException in Wildfly with a value of org.eclipse.microprofile.config.configproperty.unconfigureddvalue 25 | **/ 26 | private boolean isNullOrEmpty(String input){ 27 | return input==null || input.isEmpty() || input.equals(UNCONFIGURED_VALUE); 28 | } 29 | 30 | private static final String UNCONFIGURED_VALUE = "org.eclipse.microprofile.config.configproperty.unconfigureddvalue"; 31 | } -------------------------------------------------------------------------------- /configconverter-list/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.converter.list.ListConverter 2 | org.microprofileext.config.converter.list.ArrayConverter 3 | -------------------------------------------------------------------------------- /configsource-base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-base 12 | 13 | Microprofile Config Extensions :: Base config source 14 | A config that provides default config_ordinal override behavior 15 | 16 | 17 | -------------------------------------------------------------------------------- /configsource-base/src/main/java/org/microprofileext/config/source/base/BaseConfigSource.java: -------------------------------------------------------------------------------- 1 | // TODO FIXME directly including a Geronimo Config class due to inaccessibility 2 | /* 3 | * Licensed to the Apache Software Foundation (ASF) under one 4 | * or more contributor license agreements. See the NOTICE file 5 | * distributed with this work for additional information 6 | * regarding copyright ownership. The ASF licenses this file 7 | * to you under the Apache License, Version 2.0 (the 8 | * "License"); you may not use this file except in compliance 9 | * with the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, 14 | * software distributed under the License is distributed on an 15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | * KIND, either express or implied. See the License for the 17 | * specific language governing permissions and limitations 18 | * under the License. 19 | */ 20 | package org.microprofileext.config.source.base; 21 | 22 | import java.util.Set; 23 | import java.util.logging.Level; 24 | import java.util.logging.Logger; 25 | import org.eclipse.microprofile.config.Config; 26 | import org.eclipse.microprofile.config.spi.ConfigProviderResolver; 27 | 28 | import org.eclipse.microprofile.config.spi.ConfigSource; 29 | 30 | /** 31 | * Base class for all our ConfigSources 32 | * 33 | * @author Mark Struberg 34 | * @author Gerhard Petracek 35 | * @author Derek P. Moore 36 | */ 37 | public abstract class BaseConfigSource implements ConfigSource { 38 | private static final Logger log = Logger.getLogger(BaseConfigSource.class.getName()); 39 | 40 | private final Config config; 41 | private int ordinal = 1000; // default 42 | 43 | public BaseConfigSource(){ 44 | super(); 45 | this.config = createConfig(); 46 | } 47 | 48 | public Config getConfig(){ 49 | return this.config; 50 | } 51 | 52 | @Override 53 | public Set getPropertyNames() { 54 | return getProperties().keySet(); 55 | } 56 | 57 | @Override 58 | public int getOrdinal() { 59 | return ordinal; 60 | } 61 | 62 | /** 63 | * Init method e.g. for initializing the ordinal. 64 | * This method can be used from a subclass to determine 65 | * the ordinal value 66 | * 67 | * @param defaultOrdinal the default value for the ordinal if not set via configuration 68 | */ 69 | protected void initOrdinal(int defaultOrdinal) { 70 | ordinal = defaultOrdinal; 71 | 72 | String configuredOrdinalString = getValue(CONFIG_ORDINAL); 73 | 74 | try { 75 | if (configuredOrdinalString != null) { 76 | ordinal = Integer.parseInt(configuredOrdinalString.trim()); 77 | } 78 | } catch (NumberFormatException e) { 79 | log.log(Level.WARNING, e, 80 | () -> "The configured config-ordinal isn't a valid integer. Invalid value: " + configuredOrdinalString); 81 | } 82 | } 83 | 84 | private Config createConfig(){ 85 | return ConfigProviderResolver.instance() 86 | .getBuilder() 87 | .addDefaultSources() 88 | .build(); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /configsource-base/src/main/java/org/microprofileext/config/source/base/EnabledConfigSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Derek P. Moore. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.microprofileext.config.source.base; 17 | 18 | import java.util.Collections; 19 | import java.util.Map; 20 | import org.eclipse.microprofile.config.Config; 21 | 22 | /** 23 | * A config source that can be disabled by class or by instance (all vs each). 24 | *

25 | * Instance keys take precedence over class keys, so individual sources can be 26 | * turned back on if all sources have been turned off. 27 | *

28 | * Config sources are enabled by default. 29 | * 30 | * @author Derek P. Moore 31 | */ 32 | public abstract class EnabledConfigSource extends BaseConfigSource { 33 | 34 | /** 35 | * Called to return the properties in this config source when it is enabled 36 | * @return the map containing the properties in this config source 37 | */ 38 | abstract protected Map getPropertiesIfEnabled(); 39 | 40 | /** 41 | * Return the properties, unless disabled return empty 42 | * @return the map containing the properties in this config source or empty 43 | * if disabled 44 | */ 45 | @Override 46 | public Map getProperties() { 47 | return isEnabled() ? getPropertiesIfEnabled() : Collections.emptyMap(); 48 | } 49 | 50 | protected boolean isEnabled() { 51 | Config cnf = getConfig(); 52 | return cnf.getOptionalValue(getInstanceEnableKey(), Boolean.class) 53 | .orElse(cnf.getOptionalValue(getClassEnableKey(), Boolean.class) 54 | .orElse(true)); 55 | } 56 | 57 | protected String getClassKeyPrefix(){ 58 | return getClass().getSimpleName(); 59 | } 60 | 61 | private String getClassEnableKey(){ 62 | return getClassKeyPrefix() + ".enabled"; 63 | } 64 | 65 | private String getInstanceEnableKey(){ 66 | return getName() + ".enabled"; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /configsource-base/src/main/java/org/microprofileext/config/source/base/ExpiringMap.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.base; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * A simple map with an expiring policy. Note: this map can also store null 9 | * values! 10 | * 11 | * @param key 12 | * @param value 13 | */ 14 | public class ExpiringMap { 15 | long validity; 16 | private Map> cache = new ConcurrentHashMap<>(); 17 | 18 | public ExpiringMap(long validity) { 19 | this.validity = validity; 20 | } 21 | 22 | /** 23 | * Similar to Map::computeIfAbsent, but the mapping function can throw an 24 | * exception, which can be handled with a consumer. 25 | * 26 | * @param key key 27 | * @param action mapping function 28 | * @param onException exception handler 29 | * @return 30 | */ 31 | public V getOrCompute(K key, CheckedFunction action, Consumer onException) { 32 | TimedEntry entry = cache.get(key); 33 | if (entry == null || entry.isExpired()) { 34 | try { 35 | V value = action.apply(key); 36 | put(key, value); 37 | return value; 38 | } catch (Exception e) { 39 | onException.accept(key); 40 | } 41 | } 42 | // if the entry was never cached, then it will be null 43 | return entry != null ? entry.get() : null; 44 | } 45 | 46 | public void put(K key, V value) { 47 | cache.put(key, new TimedEntry(value)); 48 | } 49 | 50 | /** 51 | * Access underlying map, use with care 52 | * 53 | * @return 54 | */ 55 | public Map> getMap() { 56 | return cache; 57 | } 58 | 59 | @FunctionalInterface 60 | public interface CheckedFunction { 61 | R apply(T t) throws Exception; 62 | } 63 | 64 | public class TimedEntry { 65 | private final E value; 66 | private final long timestamp; 67 | 68 | public TimedEntry(E value) { 69 | this.value = value; 70 | this.timestamp = System.currentTimeMillis(); 71 | } 72 | 73 | public E get() { 74 | return value; 75 | } 76 | 77 | public boolean isExpired() { 78 | return (timestamp + validity) < System.currentTimeMillis(); 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /configsource-consul/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Consul Config Source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-consul/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-consul) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-consul.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-consul) 7 | 8 | Use [consul](https://consul.io/) to get config values. You can define the server details of the consul server using MicroProfile Config. 9 | Values are cached to reduce calls to consul. This config source does not support config events (yet). 10 | 11 | ## Usage 12 | 13 | ```xml 14 | 15 | 16 | org.microprofile-ext.config-ext 17 | configsource-consul 18 | XXXXXX 19 | runtime 20 | 21 | 22 | ``` 23 | 24 | ## Configure options 25 | 26 | configsource.consul.host (defaults to localhost) 27 | configsource.consul.prefix (default no prefix) 28 | configsource.consul.validity (default 30s) 29 | 30 | 31 | You can disable the config source by setting this config: 32 | 33 | ConsulConfigSource.enabled=false 34 | 35 | ## Links 36 | * https://github.com/rikcarve/consulkv-maven-plugin 37 | * https://microprofile.io/project/eclipse/microprofile-config 38 | -------------------------------------------------------------------------------- /configsource-consul/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-consul 12 | 13 | Microprofile Config Extensions :: Consul config source 14 | A config that gets the values from an Consul server 15 | 16 | 17 | 5.9.3 18 | 19 | 20 | 21 | 22 | com.ecwid.consul 23 | consul-api 24 | 1.4.5 25 | 26 | 27 | ${project.groupId} 28 | configsource-base 29 | ${project.version} 30 | 31 | 32 | 33 | org.junit.jupiter 34 | junit-jupiter 35 | ${junit.jupiter.version} 36 | test 37 | 38 | 39 | org.mockito 40 | mockito-core 41 | 5.3.1 42 | test 43 | 44 | 45 | org.mock-server 46 | mockserver-junit-jupiter 47 | 5.15.0 48 | test 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /configsource-consul/src/main/java/org/microprofileext/config/source/consul/Configuration.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.consul; 2 | 3 | import org.eclipse.microprofile.config.Config; 4 | import org.eclipse.microprofile.config.spi.ConfigProviderResolver; 5 | 6 | public class Configuration { 7 | 8 | private Config config = ConfigProviderResolver.instance() 9 | .getBuilder() 10 | .addDefaultSources() 11 | .build(); 12 | 13 | private String consulHost = getConfigValue("configsource.consul.host", ""); 14 | private String consulHostList = getConfigValue("configsource.consul.hosts", ""); 15 | private int consulPort = Integer.valueOf(getConfigValue("configsource.consul.port", "8500")); 16 | private long validity = Long.valueOf(getConfigValue("configsource.consul.validity", "30")) * 1000L; 17 | private String prefix = addSlash(getConfigValue("configsource.consul.prefix", "")); 18 | private String token = getConfigValue("configsource.consul.token", null); 19 | private boolean listAll = Boolean.valueOf(getConfigValue("configsource.consul.list-all", "false")); 20 | 21 | public long getValidity() { 22 | return validity; 23 | } 24 | 25 | public String getPrefix() { 26 | return prefix; 27 | } 28 | 29 | public String getToken() { 30 | return token; 31 | } 32 | 33 | public String getConsulHost() { 34 | return consulHost; 35 | } 36 | 37 | public String getConsulHostList() { 38 | return consulHostList; 39 | } 40 | 41 | public int getConsulPort() { 42 | return consulPort; 43 | } 44 | 45 | public boolean listAll() { 46 | return listAll; 47 | } 48 | 49 | private String getConfigValue(String key, String defaultValue) { 50 | return config.getOptionalValue(key, String.class).orElse(defaultValue); 51 | } 52 | 53 | private String addSlash(String envOrSystemProperty) { 54 | return envOrSystemProperty.isEmpty() ? "" : envOrSystemProperty + "/"; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /configsource-consul/src/main/java/org/microprofileext/config/source/consul/ConsulClientWrapper.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.consul; 2 | 3 | import com.ecwid.consul.v1.ConsulClient; 4 | import com.ecwid.consul.v1.Response; 5 | import com.ecwid.consul.v1.kv.model.GetValue; 6 | 7 | import java.io.UnsupportedEncodingException; 8 | import java.net.URLEncoder; 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.AbstractMap.SimpleEntry; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Map.Entry; 14 | import java.util.function.Supplier; 15 | import java.util.logging.Level; 16 | import java.util.logging.Logger; 17 | import java.util.stream.Collectors; 18 | 19 | public class ConsulClientWrapper { 20 | private static final Logger log = Logger.getLogger(ConsulClientWrapper.class.getName()); 21 | 22 | private String host; 23 | private List peers = null; 24 | private int port; 25 | private String token; 26 | ConsulClient client = null; 27 | 28 | public ConsulClientWrapper(String host, String hosts, int port, String token) { 29 | this.host = host; 30 | if (hosts != null && !hosts.isEmpty()) { 31 | peers = Arrays.asList(hosts.split(",")); 32 | } 33 | this.port = port; 34 | this.token = token; 35 | } 36 | 37 | public ConsulClient getClient() { 38 | initConsulClient(); 39 | return client; 40 | } 41 | 42 | public String getValue(String key) { 43 | try { 44 | String encodedKey = URLEncoder.encode(key, StandardCharsets.UTF_8.name()); 45 | GetValue value = retry(2, () -> getClient().getKVValue(encodedKey, token).getValue(), () -> forceReconnect()); 46 | return value == null ? null : value.getDecodedValue(); 47 | } catch (UnsupportedEncodingException e) { 48 | throw new RuntimeException(e); 49 | } catch (Exception e) { 50 | forceReconnect(); 51 | throw e; 52 | } 53 | } 54 | 55 | public List> getKeyValuePairs(String prefix) { 56 | try { 57 | String encodedPrefix = URLEncoder.encode(prefix, StandardCharsets.UTF_8.name()); 58 | List values = retry(2, () -> getClient().getKVValues(encodedPrefix, token).getValue(), () -> forceReconnect()); 59 | return values.stream() 60 | .map(v -> new SimpleEntry(v.getKey(), v.getDecodedValue())) 61 | .collect(Collectors.toList()); 62 | } catch (UnsupportedEncodingException e) { 63 | throw new RuntimeException(e); 64 | } catch (Exception e) { 65 | forceReconnect(); 66 | throw e; 67 | } 68 | } 69 | 70 | void initConsulClient() { 71 | if (peers == null) { 72 | client = new ConsulClient(host, port); 73 | peers = client.getStatusPeers().getValue().stream() 74 | .map(s -> s.split(":")[0]) 75 | .collect(Collectors.toList()); 76 | } 77 | if (client == null) { 78 | client = getClientToAnyConsulHost(); 79 | } 80 | } 81 | 82 | private ConsulClient getClientToAnyConsulHost() { 83 | return peers.stream() 84 | .map(host -> new ConsulClient(host, port)) 85 | .filter(this::isConsulReachable) 86 | .findAny() 87 | .orElseThrow(() -> new RuntimeException("No Consul host could be reached.")); 88 | } 89 | 90 | private boolean isConsulReachable(ConsulClient client) { 91 | try { 92 | Response leader = client.getStatusLeader(); 93 | log.log(Level.INFO, () -> "Successfully established connection to Consul. Current cluster leader is " + leader.getValue()); 94 | } catch (Exception e) { 95 | log.log(Level.INFO, () -> "Could not establish connection to consul: " + e.getMessage()); 96 | return false; 97 | } 98 | return true; 99 | } 100 | 101 | private T retry(int maxRetries, Supplier supplier, Runnable onFailedAttempt) { 102 | int retries = 0; 103 | RuntimeException lastException = null; 104 | while (retries <= maxRetries) { 105 | try { 106 | return supplier.get(); 107 | } catch (RuntimeException e) { 108 | lastException = e; 109 | onFailedAttempt.run(); 110 | retries++; 111 | } 112 | } 113 | throw lastException; 114 | } 115 | 116 | private void forceReconnect() { 117 | client = null; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /configsource-consul/src/main/java/org/microprofileext/config/source/consul/ConsulConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.consul; 2 | 3 | import org.microprofileext.config.source.base.EnabledConfigSource; 4 | import org.microprofileext.config.source.base.ExpiringMap; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | import java.util.Optional; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | import java.util.stream.Collectors; 13 | 14 | public class ConsulConfigSource extends EnabledConfigSource { 15 | 16 | private static final Logger log = Logger.getLogger(ConsulConfigSource.class.getName()); 17 | 18 | private static final String DEFAULT_CONSUL_CONFIGSOURCE_ORDINAL = "550"; 19 | 20 | Configuration config = new Configuration(); 21 | ExpiringMap cache = new ExpiringMap<>(config.getValidity()); 22 | boolean isDisabled = config.getConsulHost().isEmpty() && config.getConsulHostList().isEmpty(); 23 | ConsulClientWrapper client = new ConsulClientWrapper(config.getConsulHost(), config.getConsulHostList(), config.getConsulPort(), config.getToken()); 24 | 25 | public ConsulConfigSource() { 26 | log.info("Loading [consul] MicroProfile ConfigSource"); 27 | super.initOrdinal(550); 28 | } 29 | 30 | @Override 31 | public Map getPropertiesIfEnabled() { 32 | // only query for values if explicitly enabled 33 | if (!isDisabled && config.listAll()) { 34 | List> values = client.getKeyValuePairs(config.getPrefix()); 35 | values.forEach(v -> cache.put(v.getKey(), v.getValue())); 36 | } 37 | return cache.getMap().entrySet() 38 | .stream() 39 | .filter(e -> e.getValue().get() != null) 40 | .collect(Collectors.toMap( 41 | e -> e.getKey(), 42 | e -> e.getValue().get())); 43 | } 44 | 45 | @Override 46 | public String getValue(String propertyName) { 47 | if (isDisabled) { 48 | return null; 49 | } 50 | String value = cache.getOrCompute(propertyName, 51 | p -> client.getValue(config.getPrefix() + propertyName), 52 | p -> log.log(Level.FINE, () -> "consul getKV failed for key " + p)); 53 | // use default if config_ordinal not found 54 | if (CONFIG_ORDINAL.equals(propertyName)) { 55 | return Optional.ofNullable(value).orElse(DEFAULT_CONSUL_CONFIGSOURCE_ORDINAL); 56 | } 57 | return value; 58 | } 59 | 60 | @Override 61 | public String getName() { 62 | return "ConsulConfigSource"; 63 | } 64 | 65 | @Override 66 | public int getOrdinal() { 67 | return getConfig().getOptionalValue(CONFIG_ORDINAL, Integer.class).orElse(550); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /configsource-consul/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.consul.ConsulConfigSource -------------------------------------------------------------------------------- /configsource-consul/src/test/java/org/microprofileext/config/source/consul/ConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.consul; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | import static org.junit.jupiter.api.Assertions.assertNull; 7 | 8 | public class ConfigurationTest { 9 | 10 | @Test 11 | public void testGetValidity() throws Exception { 12 | Configuration config = new Configuration(); 13 | assertEquals(30000, config.getValidity()); 14 | } 15 | 16 | @Test 17 | public void testGetValidity_fromSys() throws Exception { 18 | System.setProperty("configsource.consul.validity", "10"); 19 | Configuration config = new Configuration(); 20 | assertEquals(10000, config.getValidity()); 21 | System.clearProperty("configsource.consul.validity"); 22 | } 23 | 24 | @Test 25 | public void testGetPrefix() throws Exception { 26 | Configuration config = new Configuration(); 27 | assertEquals("", config.getPrefix()); 28 | } 29 | 30 | @Test 31 | public void testGetPrefix_withSlash() throws Exception { 32 | System.setProperty("configsource.consul.prefix", "jax"); 33 | Configuration config = new Configuration(); 34 | assertEquals("jax/", config.getPrefix()); 35 | System.clearProperty("configsource.consul.prefix"); 36 | } 37 | 38 | @Test 39 | public void testGetPrefix_withSubstitution() throws Exception { 40 | System.setProperty("configsource.consul.prefix", "applications/${appname}"); 41 | System.setProperty("appname", "jax"); 42 | Configuration config = new Configuration(); 43 | assertEquals("applications/jax/", config.getPrefix()); 44 | System.clearProperty("configsource.consul.prefix"); 45 | System.clearProperty("appName"); 46 | } 47 | 48 | @Test 49 | public void testGetConsulHost() throws Exception { 50 | Configuration config = new Configuration(); 51 | assertEquals("", config.getConsulHost()); 52 | } 53 | 54 | @Test 55 | public void testGetConsulHost_fromSys() throws Exception { 56 | System.setProperty("configsource.consul.host", "jax"); 57 | Configuration config = new Configuration(); 58 | assertEquals("jax", config.getConsulHost()); 59 | System.clearProperty("configsource.consul.host"); 60 | } 61 | 62 | @Test 63 | public void testGetToken() throws Exception { 64 | System.setProperty("configsource.consul.token", "token"); 65 | Configuration config = new Configuration(); 66 | assertEquals("token", config.getToken()); 67 | System.clearProperty("configsource.consul.token"); 68 | } 69 | 70 | @Test 71 | public void testGetToken_not_configured() throws Exception { 72 | Configuration config = new Configuration(); 73 | assertNull(config.getToken()); 74 | } 75 | 76 | @Test 77 | public void testGetConsulHost_fromSys_withSubstitution() throws Exception { 78 | System.setProperty("configsource.consul.host", "${docker.host}"); 79 | System.setProperty("docker.host", "sub"); 80 | Configuration config = new Configuration(); 81 | assertEquals("sub", config.getConsulHost()); 82 | System.clearProperty("configsource.consul.host"); 83 | System.clearProperty("docker.host"); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /configsource-consul/src/test/java/org/microprofileext/config/source/consul/ConsulClientWrapperTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.consul; 2 | 3 | import com.ecwid.consul.v1.OperationException; 4 | import org.apache.http.HttpStatus; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.mockserver.integration.ClientAndServer; 9 | import org.mockserver.junit.jupiter.MockServerExtension; 10 | 11 | import java.util.List; 12 | import java.util.Map.Entry; 13 | 14 | import static org.junit.jupiter.api.Assertions.*; 15 | import static org.mockserver.model.HttpRequest.request; 16 | import static org.mockserver.model.HttpResponse.response; 17 | 18 | @ExtendWith(MockServerExtension.class) 19 | public class ConsulClientWrapperTest { 20 | 21 | ConsulClientWrapper clientWrapper; 22 | 23 | ClientAndServer clientServer; 24 | 25 | @BeforeEach 26 | public void init(ClientAndServer client) { 27 | clientServer = client; 28 | clientServer.reset(); 29 | clientServer.when(request().withPath("/v1/status/leader")).respond(response().withBody("localhost")); 30 | clientServer.when(request().withPath("/v1/status/peers")).respond(response().withBody("[\"localhost:8300\"]")); 31 | clientServer.when(request().withPath("/v1/kv/test")).respond(response().withBody("[{\"LockIndex\":0,\"Key\":\"test\",\"Flags\":0,\"Value\":\"aGVsbG8=\",\"CreateIndex\":1,\"ModifyIndex\":2}]")); 32 | clientServer.when(request().withPath("/v1/kv/testFromToken").withQueryStringParameter("token", "tokenValue")).respond(response().withBody("[{\"LockIndex\":0,\"Key\":\"testFromToken\",\"Flags\":0,\"Value\":\"aGVsbG8=\",\"CreateIndex\":1,\"ModifyIndex\":2}]")); 33 | clientServer.when(request().withPath("/v1/kv/%25dev.property.somevalue.%22com.test.app.config%22")).respond(response().withBody("[{\"LockIndex\":0,\"Key\":\"testFromToken\",\"Flags\":0,\"Value\":\"aGVsbG8=\",\"CreateIndex\":1,\"ModifyIndex\":2}]")); 34 | clientServer.when(request().withPath("/v1/kv/myapp")).respond(response().withBody("[{\"LockIndex\":0,\"Key\":\"test\",\"Flags\":0,\"Value\":\"aGVsbG8=\",\"CreateIndex\":1,\"ModifyIndex\":2}]")); 35 | clientWrapper = new ConsulClientWrapper("localhost", null, clientServer.getLocalPort(), null); 36 | } 37 | 38 | @Test 39 | public void testGetClient_singleHost() { 40 | assertNotNull(clientWrapper.getClient()); 41 | } 42 | 43 | @Test 44 | public void testGetClient_peers() { 45 | clientWrapper = new ConsulClientWrapper(null, "localhost", clientServer.getLocalPort(), null); 46 | assertNotNull(clientWrapper.getClient()); 47 | } 48 | 49 | @Test 50 | public void testGetClient_hosts_1stnotAvail() { 51 | clientWrapper = new ConsulClientWrapper(null, "localhost2,localhost", clientServer.getLocalPort(), null); 52 | assertNotNull(clientWrapper.getClient()); 53 | } 54 | 55 | @Test 56 | public void testGetValue_with_token_found() { 57 | clientWrapper = new ConsulClientWrapper("localhost", null, clientServer.getLocalPort(), "tokenValue"); 58 | String value = clientWrapper.getValue("testFromToken"); 59 | assertEquals("hello", value); 60 | } 61 | 62 | @Test 63 | public void testGetValue_found() { 64 | String value = clientWrapper.getValue("test"); 65 | assertEquals("hello", value); 66 | } 67 | 68 | @Test 69 | public void testGetValue_keys_that_need_url_encoding() { 70 | String value = clientWrapper.getValue("%dev.property.somevalue.\"com.test.app.config\""); 71 | assertEquals("hello", value); 72 | } 73 | 74 | @Test 75 | public void testGetValue_not_found() { 76 | String value = clientWrapper.getValue("nope"); 77 | assertNull(value); 78 | } 79 | 80 | @Test 81 | public void testGetKeyValuePairs() { 82 | List> value = clientWrapper.getKeyValuePairs("myapp"); 83 | assertEquals(1, value.size()); 84 | } 85 | 86 | @Test 87 | public void testGetValue_force_reconnect() { 88 | clientServer.clear(request().withPath("/v1/kv/test")); 89 | clientServer.when(request().withPath("/v1/kv/test")).respond(response().withStatusCode(HttpStatus.SC_SERVICE_UNAVAILABLE)); 90 | assertThrows(OperationException.class, () -> clientWrapper.getValue("test")); 91 | clientServer.clear(request().withPath("/v1/kv/test")); 92 | clientServer.when(request().withPath("/v1/kv/test")).respond(response().withBody("[{\"LockIndex\":0,\"Key\":\"test\",\"Flags\":0,\"Value\":\"aGVsbG8=\",\"CreateIndex\":1,\"ModifyIndex\":2}]")); 93 | String value = clientWrapper.getValue("test"); 94 | assertEquals("hello", value); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /configsource-consul/src/test/java/org/microprofileext/config/source/consul/ConsulConfigSourceTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.consul; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.AbstractMap.SimpleEntry; 7 | import java.util.Arrays; 8 | 9 | import static org.junit.jupiter.api.Assertions.*; 10 | import static org.mockito.ArgumentMatchers.anyString; 11 | import static org.mockito.Mockito.*; 12 | 13 | class ConsulConfigSourceTest { 14 | 15 | private ConsulConfigSource configSource; 16 | 17 | @BeforeEach 18 | public void init() { 19 | System.setProperty("configsource.consul.host", "localhost"); 20 | configSource = new ConsulConfigSource(); 21 | configSource.config = new Configuration(); 22 | configSource.client = mock(ConsulClientWrapper.class); 23 | } 24 | 25 | @Test 26 | void testGetName() { 27 | assertEquals("ConsulConfigSource", configSource.getName()); 28 | } 29 | 30 | @Test 31 | void testGetProperties_empty() { 32 | ConsulConfigSource configSource = new ConsulConfigSource(); 33 | assertTrue(configSource.getProperties().isEmpty()); 34 | } 35 | 36 | @Test 37 | void testGetProperties_one_from_cache() { 38 | when(configSource.client.getValue(anyString())).thenReturn("hello"); 39 | configSource.getValue("test"); 40 | assertEquals(1, configSource.getProperties().size()); 41 | } 42 | 43 | @Test 44 | void testGetProperties_from_consul() { 45 | System.setProperty("configsource.consul.list-all", "true"); 46 | configSource.config = new Configuration(); 47 | when(configSource.client.getKeyValuePairs(anyString())).thenReturn(Arrays.asList(new SimpleEntry("test", "hello"))); 48 | assertEquals(1, configSource.getProperties().size()); 49 | System.clearProperty("configsource.consul.list-all"); 50 | } 51 | 52 | @Test 53 | void testGetProperties_with_null() { 54 | when(configSource.client.getValue(anyString())).thenReturn(null); 55 | configSource.getValue("test"); 56 | assertEquals(0, configSource.getProperties().size()); 57 | } 58 | 59 | @Test 60 | void testGetValue_disabled_return_null() { 61 | System.clearProperty("configsource.consul.host"); 62 | assertNull(configSource.getValue("test")); 63 | } 64 | 65 | @Test 66 | void testGetValue_null() { 67 | when(configSource.client.getValue(anyString())).thenReturn(null); 68 | assertNull(configSource.getValue("test")); 69 | } 70 | 71 | @Test 72 | void testGetValue() { 73 | when(configSource.client.getValue(anyString())).thenReturn("hello"); 74 | assertEquals("hello", configSource.getValue("test")); 75 | } 76 | 77 | @Test 78 | void testGetValue_token_configured() { 79 | System.setProperty("configsource.consul.token", "token"); 80 | configSource.config = new Configuration(); 81 | when(configSource.client.getValue(anyString())).thenReturn("hello"); 82 | assertEquals("hello", configSource.getValue("test")); 83 | System.clearProperty("configsource.consul.token"); 84 | } 85 | 86 | @Test 87 | void testGetValue_cache() { 88 | when(configSource.client.getValue(anyString())).thenReturn("hello"); 89 | configSource.getValue("test"); 90 | configSource.getValue("test"); 91 | verify(configSource.client, times(1)).getValue(anyString()); 92 | } 93 | 94 | @Test 95 | void testGetValue_exception() { 96 | when(configSource.client.getValue(anyString())).thenThrow(RuntimeException.class); 97 | assertNull(configSource.getValue("test")); 98 | } 99 | 100 | @Test 101 | void testOrdinal_default() { 102 | when(configSource.client.getValue(anyString())).thenReturn(null); 103 | assertEquals(550, configSource.getOrdinal()); 104 | } 105 | 106 | //@Test 107 | //void testOrdinal_overwrite() { 108 | // when(configSource.client.getValue(anyString())).thenReturn("200"); 109 | // assertEquals(200, configSource.getOrdinal()); 110 | //} 111 | 112 | } 113 | -------------------------------------------------------------------------------- /configsource-db/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Database config source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-db/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-db) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-db.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-db) 7 | 8 | An Eclipse MicroProfile config extension which uses a database as source. 9 | 10 | ## Overview 11 | The eclipse microprofile config framework is a simple yet powerful configuration framework for Java EE. But most implementations only provide the system/env properties or property files as configuration source. This small library provides an ConfigSource implementation which reads the values from the default datasource. For performance reasons, the config values are cached. 12 | 13 | > This config source expects JNDI & JDBC to be available. The datasource are looked up 14 | 15 | ## Usage 16 | ```xml 17 | 18 | org.microprofile-ext.config-ext 19 | configsource-db 20 | 1.x 21 | 22 | ``` 23 | 24 | ## Configuration 25 | Currently there are 5 values you can configure, either through Java system properties or environment variables: 26 | * **configsource.db.datasource** override ee default datasource by setting JNDI name of the datasource 27 | * **configsource.db.table** table name for configuration records, default value is "configuration" 28 | * **configsource.db.key-column** name of the column containing the key, default value is "key" 29 | * **configsource.db.value-column** name of the column containing the value, default value is "value" 30 | * **configsource.db.validity** how long to cache values (in seconds), default is 30s 31 | 32 | ### Events 33 | 34 | This config source fires CDI Events on changes 35 | 36 | Read more about [Config Events](https://github.com/microprofile-extensions/config-ext/blob/master/config-events/README.md) 37 | 38 | You can disable this with the `configsource.db.notifyOnChanges` property: 39 | 40 | configsource.db.notifyOnChanges=false 41 | 42 | ## Links 43 | * https://microprofile.io/project/eclipse/microprofile-config 44 | * https://github.com/rikcarve/consulkv-maven-plugin 45 | * https://github.com/rikcarve/mp-config-consul 46 | * https://github.com/microprofile-extensions 47 | 48 | -------------------------------------------------------------------------------- /configsource-db/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.microprofile-ext 5 | config-ext 6 | 3.0.2-SNAPSHOT 7 | 8 | 9 | org.microprofile-ext.config-ext 10 | configsource-db 11 | 12 | Microprofile Config Extensions :: Database config source 13 | A config that get the values from a datasource 14 | 15 | 16 | 5.9.3 17 | 18 | 19 | 20 | 21 | ${project.groupId} 22 | configsource-base 23 | ${project.version} 24 | 25 | 26 | 27 | org.junit.jupiter 28 | junit-jupiter 29 | ${junit.jupiter.version} 30 | test 31 | 32 | 33 | org.mockito 34 | mockito-core 35 | 5.3.1 36 | test 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-surefire-plugin 46 | 3.1.0 47 | 48 | 49 | org.jacoco 50 | jacoco-maven-plugin 51 | 0.8.10 52 | 53 | 54 | 55 | prepare-agent 56 | 57 | 58 | 59 | report 60 | test 61 | 62 | report 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /configsource-db/src/main/java/org/microprofileext/config/source/db/DatasourceConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.db; 2 | 3 | import org.eclipse.microprofile.config.Config; 4 | import org.microprofileext.config.source.base.EnabledConfigSource; 5 | import org.microprofileext.config.source.base.ExpiringMap; 6 | 7 | import java.sql.SQLException; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.logging.Logger; 11 | 12 | public class DatasourceConfigSource extends EnabledConfigSource { 13 | 14 | private static final Logger log = Logger.getLogger(DatasourceConfigSource.class.getName()); 15 | 16 | private static final String VALIDITY_KEY = "configsource.db.validity"; 17 | private static final String CONFIGSOURCE_ORDINAL_KEY = "configsource.db.ordinal"; 18 | 19 | private Config config; 20 | Repository repository = null; 21 | ExpiringMap cache = null; 22 | 23 | public DatasourceConfigSource() { 24 | config = getConfig(); 25 | log.info("Loading [db] MicroProfile ConfigSource"); 26 | super.initOrdinal(450); 27 | } 28 | 29 | @Override 30 | protected Map getPropertiesIfEnabled() { 31 | initRepository(); 32 | try { 33 | return repository.getAllConfigValues(); 34 | } catch (SQLException e) { 35 | log.info("query failed: " + e.getMessage()); 36 | clearRepository(); 37 | } 38 | return new HashMap<>(); 39 | } 40 | 41 | @Override 42 | public String getValue(String propertyName) { 43 | initRepository(); 44 | initCache(); 45 | 46 | return cache.getOrCompute(propertyName, p -> repository.getConfigValue(p), (e) -> clearRepository()); 47 | } 48 | 49 | @Override 50 | public String getName() { 51 | return "DatasourceConfigSource"; 52 | } 53 | 54 | @Override 55 | public int getOrdinal() { 56 | return config.getOptionalValue(CONFIGSOURCE_ORDINAL_KEY, Integer.class).orElse(450); 57 | } 58 | 59 | private void initRepository() { 60 | if (repository == null) { 61 | // late initialization is needed because of the EE datasource. 62 | repository = new Repository(config); 63 | } 64 | } 65 | 66 | void clearRepository() { 67 | repository = null; 68 | } 69 | 70 | private void initCache() { 71 | if (cache == null) { 72 | long validity = config.getOptionalValue(VALIDITY_KEY, Long.class).orElse(30000L); 73 | cache = new ExpiringMap<>(validity); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /configsource-db/src/main/java/org/microprofileext/config/source/db/Repository.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.db; 2 | 3 | import java.sql.PreparedStatement; 4 | import java.sql.ResultSet; 5 | import java.sql.SQLException; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import javax.naming.InitialContext; 12 | import javax.naming.NamingException; 13 | import javax.sql.DataSource; 14 | 15 | import org.eclipse.microprofile.config.Config; 16 | 17 | public class Repository { 18 | private static final Logger log = Logger.getLogger(Repository.class.getName()); 19 | 20 | private static final String KEY_PREFIX = "configsource.db."; 21 | private static final String KEY_DATASOURCE = KEY_PREFIX + "datasource"; 22 | private static final String KEY_TABLE = KEY_PREFIX + "table"; 23 | private static final String KEY_KEY_COLUMN = KEY_PREFIX + "key-column"; 24 | private static final String KEY_VALUE_COLUMN = KEY_PREFIX + "value-column"; 25 | 26 | PreparedStatement selectOne = null; 27 | PreparedStatement selectAll = null; 28 | 29 | public Repository(Config config) { 30 | DataSource datasource = getDatasource(config.getOptionalValue(KEY_DATASOURCE, String.class).orElse("java:comp/DefaultDataSource")); 31 | String table = config.getOptionalValue(KEY_TABLE, String.class).orElse("configuration"); 32 | String keyColumn = config.getOptionalValue(KEY_KEY_COLUMN, String.class).orElse("key"); 33 | String valueColumn = config.getOptionalValue(KEY_VALUE_COLUMN, String.class).orElse("value"); 34 | if (datasource != null) { 35 | String queryOne = "select " + valueColumn + " from " +table + " where " + keyColumn + " = ?"; 36 | String queryAll = "select " + keyColumn + ", " + valueColumn + " from " + table; 37 | try { 38 | selectOne = datasource.getConnection().prepareStatement(queryOne); 39 | selectAll = datasource.getConnection().prepareStatement(queryAll); 40 | } catch (SQLException e) { 41 | log.log(Level.FINE, () -> "Configuration query could not be prepared: " + e.getMessage()); 42 | } 43 | } 44 | } 45 | 46 | public synchronized Map getAllConfigValues() throws SQLException { 47 | Map result = new HashMap<>(); 48 | if (selectAll != null) { 49 | try (ResultSet rs = selectAll.executeQuery()) { 50 | while (rs.next()) { 51 | result.put(rs.getString(1), rs.getString(2)); 52 | } 53 | } 54 | } 55 | return result; 56 | } 57 | 58 | public synchronized String getConfigValue(String key) throws SQLException { 59 | if (selectOne != null) { 60 | selectOne.setString(1, key); 61 | try (ResultSet rs = selectOne.executeQuery()) { 62 | if (rs.next()) { 63 | return rs.getString(1); 64 | } 65 | } 66 | } else { 67 | throw new SQLException("datasource not initialized"); 68 | } 69 | return null; 70 | } 71 | 72 | private DataSource getDatasource(String jndi) { 73 | try { 74 | return InitialContext.doLookup(jndi); 75 | } catch (NamingException e) { 76 | log.log(Level.WARNING, () -> "Could not get datasource: " + e.getMessage()); 77 | return null; 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /configsource-db/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.db.DatasourceConfigSource 2 | -------------------------------------------------------------------------------- /configsource-db/src/test/java/org/microprofileext/config/source/db/DatasourceConfigSourceTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.db; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.microprofileext.config.source.base.ExpiringMap; 6 | import org.mockito.Mockito; 7 | 8 | import java.sql.SQLException; 9 | import java.util.Collections; 10 | 11 | import static org.junit.jupiter.api.Assertions.*; 12 | import static org.mockito.ArgumentMatchers.anyString; 13 | import static org.mockito.Mockito.*; 14 | 15 | class DatasourceConfigSourceTest { 16 | 17 | private DatasourceConfigSource configSource; 18 | 19 | @BeforeEach 20 | public void init() { 21 | configSource = Mockito.spy(new DatasourceConfigSource()); 22 | configSource.repository = mock(Repository.class); 23 | configSource.cache = new ExpiringMap(30000); 24 | } 25 | 26 | @Test 27 | void testGetOrdinal() { 28 | assertTrue(configSource.getOrdinal() > 100); 29 | } 30 | 31 | @Test 32 | void testGetProperties_empty() { 33 | assertTrue(configSource.getProperties().isEmpty()); 34 | } 35 | 36 | @Test 37 | public void testGetProperties_null() throws Exception { 38 | when(configSource.repository.getConfigValue(anyString())).thenReturn(null); 39 | configSource.getValue("test"); 40 | assertTrue(configSource.getProperties().isEmpty()); 41 | } 42 | 43 | @Test 44 | public void testGetProperties_one() throws Exception { 45 | when(configSource.repository.getAllConfigValues()).thenReturn(Collections.singletonMap("test", "value")); 46 | configSource.getValue("test"); 47 | assertEquals(1, configSource.getProperties().size()); 48 | } 49 | 50 | @Test 51 | public void testGetValue() throws Exception { 52 | when(configSource.repository.getConfigValue(anyString())).thenReturn("123"); 53 | assertEquals("123", configSource.getValue("test")); 54 | } 55 | 56 | @Test 57 | public void testGetValue_cache() throws Exception { 58 | when(configSource.repository.getConfigValue(anyString())).thenReturn("123"); 59 | configSource.getValue("test"); 60 | configSource.getValue("test"); 61 | verify(configSource.repository, times(1)).getConfigValue(anyString()); 62 | } 63 | 64 | @Test 65 | public void testGetValue_exception() throws Exception { 66 | when(configSource.repository.getConfigValue(anyString())).thenThrow(SQLException.class); 67 | assertNull(configSource.getValue("test")); 68 | verify(configSource, times(1)).clearRepository(); 69 | } 70 | 71 | @Test 72 | public void testGetValue_exception_cache() throws Exception { 73 | 74 | // negative value, otherwise two serial "getValue" will have the same timestamp 75 | // and the entry is not yet expired. 76 | configSource.cache = new ExpiringMap(-5); 77 | 78 | when(configSource.repository.getConfigValue(anyString())).thenReturn("123"); 79 | assertEquals("123", configSource.getValue("test")); 80 | when(configSource.repository.getConfigValue(anyString())).thenThrow(SQLException.class); 81 | assertEquals("123", configSource.getValue("test")); // load from cache, also when the entry is already expired. 82 | verify(configSource, times(1)).clearRepository(); 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /configsource-db/src/test/java/org/microprofileext/config/source/db/RepositoryTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.db; 2 | 3 | import org.eclipse.microprofile.config.Config; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.mockito.Mockito; 7 | 8 | import java.sql.PreparedStatement; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.util.Optional; 12 | 13 | import static org.junit.jupiter.api.Assertions.*; 14 | import static org.mockito.Mockito.mock; 15 | import static org.mockito.Mockito.when; 16 | 17 | class RepositoryTest { 18 | 19 | Repository repository; 20 | 21 | @BeforeEach 22 | public void init() { 23 | Config config = mock(Config.class); 24 | when(config.getOptionalValue(Mockito.anyString(), Mockito.any())).thenReturn(Optional.empty()); 25 | repository = new Repository(config); 26 | } 27 | 28 | @Test 29 | void testGetConfigValue_exception() throws SQLException { 30 | repository.selectOne = mock(PreparedStatement.class); 31 | when(repository.selectOne.executeQuery()).thenThrow(SQLException.class); 32 | assertThrows(SQLException.class, () -> repository.getConfigValue("test")); 33 | } 34 | 35 | @Test 36 | void testGetConfigValue_none() throws SQLException { 37 | repository.selectOne = mock(PreparedStatement.class); 38 | ResultSet rs = mock(ResultSet.class); 39 | when(rs.next()).thenReturn(false); 40 | when(repository.selectOne.executeQuery()).thenReturn(rs); 41 | assertNull(repository.getConfigValue("test")); 42 | } 43 | 44 | @Test 45 | void testGetConfigValue_no_stmt() throws SQLException { 46 | assertThrows(SQLException.class, () -> repository.getConfigValue("test")); 47 | } 48 | 49 | @Test 50 | void testGetConfigValue() throws SQLException { 51 | repository.selectOne = mock(PreparedStatement.class); 52 | ResultSet rs = mock(ResultSet.class); 53 | when(rs.next()).thenReturn(true); 54 | when(rs.getString(1)).thenReturn("value"); 55 | when(repository.selectOne.executeQuery()).thenReturn(rs); 56 | assertEquals("value", repository.getConfigValue("test")); 57 | } 58 | 59 | @Test 60 | void testGetAllConfigValues_no_stmt() throws SQLException { 61 | assertEquals(0, repository.getAllConfigValues().size()); 62 | } 63 | 64 | @Test 65 | void testGetAllConfigValues_exception() throws SQLException { 66 | repository.selectAll = mock(PreparedStatement.class); 67 | when(repository.selectAll.executeQuery()).thenThrow(SQLException.class); 68 | assertThrows(SQLException.class, () -> repository.getAllConfigValues().size()); 69 | } 70 | 71 | @Test 72 | void testGetAllConfigValues() throws SQLException { 73 | repository.selectAll = mock(PreparedStatement.class); 74 | ResultSet rs = mock(ResultSet.class); 75 | when(rs.next()).thenReturn(true).thenReturn(false); 76 | when(rs.getString(1)).thenReturn("test"); 77 | when(rs.getString(2)).thenReturn("value"); 78 | when(repository.selectAll.executeQuery()).thenReturn(rs); 79 | assertEquals(1, repository.getAllConfigValues().size()); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /configsource-etcd/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Etcd Config Source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-etcd/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-etcd) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-etcd.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-etcd) 7 | 8 | Use [etcd](https://coreos.com/etcd/) to get config values. You can define the server details of the etcd server using MicroProfile Config: 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-etcd 17 | XXXXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Configure options 24 | 25 | configsource.etcd.scheme=http (default) 26 | configsource.etcd.host=localhost (default) 27 | configsource.etcd.port=2379 (default) 28 | configsource.etcd.user (default no user) 29 | configsource.etcd.password (default no password) 30 | configsource.etcd.authority (default no authority) 31 | 32 | 33 | You can disable the config source by setting this config: 34 | 35 | EtcdConfigSource.enabled=false 36 | -------------------------------------------------------------------------------- /configsource-etcd/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-etcd 12 | 13 | Microprofile Config Extensions :: Etcd config source 14 | A config that get the values from an Etcd server 15 | 16 | 17 | 18 | 19 | com.coreos 20 | jetcd-core 21 | 0.0.2 22 | 23 | 24 | 25 | ${project.groupId} 26 | configsource-base 27 | ${project.version} 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /configsource-etcd/src/main/java/org/microprofileext/config/source/etcd/EtcdConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.etcd; 2 | 3 | import com.coreos.jetcd.Client; 4 | import com.coreos.jetcd.ClientBuilder; 5 | import com.coreos.jetcd.data.ByteSequence; 6 | import com.coreos.jetcd.data.KeyValue; 7 | import com.coreos.jetcd.kv.GetResponse; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.concurrent.CompletableFuture; 12 | import java.util.concurrent.ExecutionException; 13 | import java.util.logging.Level; 14 | import java.util.logging.Logger; 15 | import org.microprofileext.config.source.base.EnabledConfigSource; 16 | 17 | /** 18 | * Etcd config source 19 | * @author Phillip Kruger 20 | * 21 | * TODO: Add watcher 22 | * ByteSequence bsKey = ByteSequence.fromString(EMPTY); 23 | * Watch.Watcher watch = this.client.getWatchClient().watch(bsKey); 24 | * watch.listen(); 25 | */ 26 | public class EtcdConfigSource extends EnabledConfigSource { 27 | 28 | private static final Logger log = Logger.getLogger(EtcdConfigSource.class.getName()); 29 | 30 | private static final String NAME = "EtcdConfigSource"; 31 | 32 | private static final String KEY_PREFIX = "configsource.etcd."; 33 | 34 | private static final String KEY_SCHEME = KEY_PREFIX + "scheme"; 35 | private static final String DEFAULT_SCHEME = "http"; 36 | 37 | private static final String KEY_HOST = KEY_PREFIX + "host"; 38 | private static final String DEFAULT_HOST = "localhost"; 39 | 40 | private static final String KEY_PORT = KEY_PREFIX + "port"; 41 | private static final Integer DEFAULT_PORT = 2379; 42 | 43 | private static final String KEY_USER = KEY_PREFIX + "user"; 44 | private static final String KEY_PASSWORD = KEY_PREFIX + "password"; 45 | private static final String KEY_AUTHORITY = KEY_PREFIX + "authority"; 46 | 47 | private Client client = null; 48 | 49 | public EtcdConfigSource(){ 50 | super.initOrdinal(320); 51 | } 52 | 53 | @Override 54 | public Map getPropertiesIfEnabled() { 55 | Map m = new HashMap<>(); 56 | 57 | ByteSequence bsKey = ByteSequence.fromString(EMPTY); 58 | 59 | CompletableFuture getFuture = getClient().getKVClient().get(bsKey); 60 | try { 61 | GetResponse response = getFuture.get(); 62 | List kvs = response.getKvs(); 63 | 64 | for(KeyValue kv:kvs){ 65 | String key = kv.getKey().toStringUtf8(); 66 | String value = kv.getValue().toStringUtf8(); 67 | m.put(key, value); 68 | } 69 | } catch (InterruptedException | ExecutionException ex) { 70 | log.log(Level.FINEST, "Can not get all config keys and values from etcd Config source: {1}", new Object[]{ex.getMessage()}); 71 | } 72 | 73 | return m; 74 | } 75 | 76 | @Override 77 | public String getValue(String key) { 78 | if (key.startsWith(KEY_PREFIX)) { 79 | // in case we are about to configure ourselves we simply ignore that key 80 | return null; 81 | } 82 | if(super.isEnabled()){ 83 | ByteSequence bsKey = ByteSequence.fromString(key); 84 | CompletableFuture getFuture = getClient().getKVClient().get(bsKey); 85 | try { 86 | GetResponse response = getFuture.get(); 87 | String value = toString(response); 88 | return value; 89 | } catch (InterruptedException | ExecutionException ex) { 90 | log.log(Level.FINEST, "Can not get config value for [{0}] from etcd Config source: {1}", new Object[]{key, ex.getMessage()}); 91 | } 92 | } 93 | return null; 94 | } 95 | 96 | @Override 97 | public String getName() { 98 | return NAME; 99 | } 100 | 101 | private String toString(GetResponse response){ 102 | if(response.getCount()>0){ 103 | return response.getKvs().get(0).getValue().toStringUtf8(); 104 | } 105 | return null; 106 | } 107 | 108 | private Client getClient(){ 109 | if(this.client == null ){ 110 | log.info("Loading [etcd] MicroProfile ConfigSource"); 111 | 112 | String scheme = getConfig().getOptionalValue(KEY_SCHEME, String.class).orElse(DEFAULT_SCHEME); 113 | String host = getConfig().getOptionalValue(KEY_HOST, String.class).orElse(DEFAULT_HOST); 114 | Integer port = getConfig().getOptionalValue(KEY_PORT, Integer.class).orElse(DEFAULT_PORT); 115 | 116 | String user = getConfig().getOptionalValue(KEY_USER, String.class).orElse(null); 117 | String password = getConfig().getOptionalValue(KEY_PASSWORD, String.class).orElse(null); 118 | String authority = getConfig().getOptionalValue(KEY_AUTHORITY, String.class).orElse(null); 119 | 120 | String endpoint = String.format("%s://%s:%d",scheme,host,port); 121 | log.log(Level.INFO, "Using [{0}] as etcd server endpoint", endpoint); 122 | 123 | ClientBuilder clientBuilder = Client.builder().endpoints(endpoint); 124 | if(user!=null){ 125 | ByteSequence bsUser = ByteSequence.fromString(user); 126 | clientBuilder = clientBuilder.user(bsUser); 127 | } 128 | if(password!=null){ 129 | ByteSequence bsPassword = ByteSequence.fromString(password); 130 | clientBuilder = clientBuilder.password(bsPassword); 131 | } 132 | if(authority!=null){ 133 | clientBuilder = clientBuilder.authority(authority); 134 | } 135 | 136 | this.client = clientBuilder.build(); 137 | } 138 | return this.client; 139 | } 140 | 141 | private static final String EMPTY = ""; 142 | 143 | } 144 | -------------------------------------------------------------------------------- /configsource-etcd/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.etcd.EtcdConfigSource -------------------------------------------------------------------------------- /configsource-filebase/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-filebase 12 | 13 | Microprofile Config Extensions :: Filebase config source 14 | A config that provides default behavior for file based sources 15 | 16 | 17 | 18 | ${project.groupId} 19 | configsource-base 20 | ${project.version} 21 | 22 | 23 | ${project.groupId} 24 | config-events 25 | ${project.version} 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /configsource-filebase/src/main/java/org/microprofileext/config/source/base/file/FileResourceWatcher.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.base.file; 2 | 3 | import java.io.IOException; 4 | import java.net.MalformedURLException; 5 | import java.net.URISyntaxException; 6 | import java.net.URL; 7 | import java.nio.file.FileSystems; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | import java.nio.file.StandardWatchEventKinds; 11 | import java.nio.file.WatchEvent; 12 | import java.nio.file.WatchKey; 13 | import java.nio.file.WatchService; 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.ScheduledExecutorService; 20 | import java.util.concurrent.TimeUnit; 21 | import java.util.logging.Level; 22 | import java.util.logging.Logger; 23 | 24 | /** 25 | * Watching files for changes 26 | * @author Phillip Kruger 27 | */ 28 | public class FileResourceWatcher { 29 | 30 | private static final Logger log = Logger.getLogger(FileResourceWatcher.class.getName()); 31 | 32 | private WatchService watcher; 33 | 34 | private final Map directoryWatchers = new HashMap<>(); 35 | private final Map> filterMap = new HashMap<>(); 36 | private final long pollInterval; 37 | private final Reloadable reloadable; 38 | 39 | private final ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor(); 40 | 41 | public FileResourceWatcher(Reloadable reloadable, long pollInterval){ 42 | this.reloadable = reloadable; 43 | this.pollInterval = pollInterval; 44 | 45 | try { 46 | this.watcher = FileSystems.getDefault().newWatchService(); 47 | } catch (IOException ex) { 48 | log.log(Level.SEVERE, null, ex); 49 | } 50 | } 51 | 52 | public void startWatching(URL url){ 53 | try { 54 | Path path = Paths.get(url.toURI()); 55 | Path dir = path.getParent(); 56 | String filename = path.getFileName().toString(); 57 | startWatching(dir,filename); 58 | } catch (URISyntaxException ex) { 59 | log.log(Level.WARNING, "Can not watch url [" + url +"]", ex); 60 | } 61 | } 62 | 63 | private void startWatching(Path path,String filter){ 64 | if(filterMap.containsKey(path)){ 65 | // Already watching this directory 66 | if(!filterMap.get(path).contains(filter))filterMap.get(path).add(filter); 67 | }else { 68 | // New folder to monitor 69 | List filters = new ArrayList<>(); 70 | filters.add(filter); 71 | filterMap.put(path, filters); 72 | 73 | try { 74 | WatchKey key = path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY); 75 | directoryWatchers.put(key, path); 76 | // Here start Runable 77 | scheduledThreadPool.schedule(new Poller(),this.pollInterval,TimeUnit.SECONDS); 78 | } catch (IOException ex) { 79 | log.log(Level.WARNING, "Could not register directory [{0}] to watch for changes - {1}", new Object[]{path, ex.getMessage()}); 80 | } 81 | } 82 | } 83 | 84 | @SuppressWarnings("unchecked") 85 | private WatchEvent cast(WatchEvent event) { 86 | return (WatchEvent)event; 87 | } 88 | 89 | class Poller implements Runnable{ 90 | 91 | @Override 92 | public void run() { 93 | WatchKey key; 94 | try { 95 | key = watcher.take(); 96 | } catch (InterruptedException x) { 97 | return; 98 | } 99 | 100 | Path d = directoryWatchers.get(key); 101 | if (d != null) { 102 | 103 | for (WatchEvent event: key.pollEvents()) { 104 | WatchEvent.Kind kind = event.kind(); 105 | if (kind == StandardWatchEventKinds.OVERFLOW)continue; 106 | 107 | WatchEvent ev = cast(event); 108 | Path name = ev.context(); 109 | List filters = filterMap.get(d); 110 | 111 | if(filters.contains(name.toString())){ 112 | Path child = d.resolve(name); 113 | try { 114 | reloadable.reload(child.toUri().toURL()); 115 | } catch (MalformedURLException ex) { 116 | log.log(Level.SEVERE, null, ex); 117 | } 118 | } 119 | } 120 | } 121 | 122 | boolean valid = key.reset(); 123 | if (!valid)directoryWatchers.remove(key); 124 | 125 | this.run(); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /configsource-filebase/src/main/java/org/microprofileext/config/source/base/file/Reloadable.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.base.file; 2 | 3 | import java.net.URL; 4 | 5 | /** 6 | * Marks a class as reloadable 7 | * @author Phillip Kruger 8 | */ 9 | public interface Reloadable { 10 | public void reload(URL url); 11 | } 12 | -------------------------------------------------------------------------------- /configsource-filebase/src/main/java/org/microprofileext/config/source/base/file/WebResourceWatcher.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.base.file; 2 | 3 | import java.io.IOException; 4 | import java.net.HttpURLConnection; 5 | import java.net.URL; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.ScheduledExecutorService; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.logging.Level; 13 | import java.util.logging.Logger; 14 | 15 | /** 16 | * Watching web resources for changes 17 | * @author Phillip Kruger 18 | */ 19 | public class WebResourceWatcher { 20 | 21 | private static final Logger log = Logger.getLogger(WebResourceWatcher.class.getName()); 22 | 23 | private final long pollInterval; 24 | private final Reloadable reloadable; 25 | 26 | private final ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor(); 27 | 28 | private final Map urlsToWatch = new HashMap<>(); 29 | 30 | public WebResourceWatcher(Reloadable reloadable, long pollInterval){ 31 | this.reloadable = reloadable; 32 | this.pollInterval = pollInterval; 33 | scheduledThreadPool.scheduleAtFixedRate(new Poller(),this.pollInterval,this.pollInterval,TimeUnit.SECONDS); 34 | } 35 | 36 | public void startWatching(URL url){ 37 | if(!urlsToWatch.containsKey(url)){ 38 | long lastModified = getLastModified(url); 39 | if(lastModified>0){ 40 | urlsToWatch.put(url,lastModified); 41 | }else{ 42 | log.log(Level.WARNING, "Can not poll {0} for changes, lastModified not implemented", url); 43 | } 44 | } 45 | } 46 | 47 | private long getLastModified(URL url){ 48 | HttpURLConnection con = null; 49 | try { 50 | con = (HttpURLConnection) url.openConnection(); 51 | con.setRequestMethod(HEAD); 52 | con.setConnectTimeout(5000); 53 | con.setReadTimeout(5000); 54 | return con.getLastModified(); 55 | } catch (IOException ex) { 56 | log.log(Level.SEVERE, ex.getMessage()); 57 | } finally { 58 | if(con!=null)con.disconnect(); 59 | } 60 | 61 | return -1; 62 | } 63 | 64 | private static final String HEAD = "HEAD"; 65 | 66 | class Poller implements Runnable{ 67 | 68 | @Override 69 | public void run() { 70 | Set urls = urlsToWatch.keySet(); 71 | for(URL url : urls){ 72 | long lastModified = getLastModified(url); 73 | if(lastModified!=urlsToWatch.get(url)){ 74 | urlsToWatch.put(url, lastModified); 75 | reloadable.reload(url); 76 | } 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /configsource-json/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Json Config Source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-json/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-json) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-json.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-json) 7 | 8 | This source gets values from some json file(s). 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-json 17 | XXXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Example: 24 | 25 | ```json 26 | 27 | { 28 | "location": { 29 | "protocol": "http", 30 | "host": "localhost", 31 | "port": 8080, 32 | "path": "/some/path", 33 | "jedis": [ 34 | "Yoda", 35 | "Qui-Gon Jinn", 36 | "Obi-Wan Kenobi", 37 | "Luke Skywalker" 38 | ] 39 | } 40 | } 41 | 42 | ``` 43 | 44 | will create the following properties: 45 | 46 | ```property 47 | 48 | "location.protocol": "http" 49 | "location.host": "localhost" 50 | "location.port": "8080" 51 | "location.path": "/some/path" 52 | "location.jedis": "Yoda, Qui-Gon Jinn, Obi-Wan Kenobi, Luke Skywalker" 53 | 54 | ``` 55 | 56 | 57 | You can `inject` the jedis using any of the following: 58 | 59 | ```java 60 | 61 | @Inject 62 | @ConfigProperty(name = "location.jedis") 63 | String jedisAsString; 64 | 65 | @Inject 66 | @ConfigProperty(name = "location.jedis") 67 | List jedisAsList; 68 | 69 | @Inject 70 | @ConfigProperty(name = "location.jedis") 71 | Set jedisAsSet; 72 | 73 | @Inject 74 | @ConfigProperty(name = "location.jedis") 75 | String[] jedisAsArray; 76 | 77 | ``` 78 | 79 | ## Configure options 80 | 81 | ### Url(s) 82 | 83 | By default the config source will look for a file called `application.json`. You can set the location(s) of the files: 84 | 85 | configsource.json.url= 86 | 87 | example: 88 | 89 | configsource.json.url=file:/tmp/myconfig.json 90 | 91 | You can also add more than one location by comma-separating the location: 92 | 93 | configsource.json.url=file:/tmp/myconfig.json,http://localhost/myconfig.json 94 | 95 | The latest files will override properties in previous files. As example, if using above configuration, property `foo=bar` in `file:/tmp/myconfig.json` will be override if it's added to `http://localhost/myconfig.json`. 96 | 97 | ### Detecting changes. 98 | 99 | You can watch the resource for changes. This feature is disabled by default. To enable: 100 | 101 | configsource.json.pollForChanges=true 102 | 103 | By default it will poll every **5 seconds**. You can change that, example to poll every 5 minutes: 104 | 105 | configsource.json.pollInterval=300 106 | 107 | ### Events 108 | 109 | This config source fires CDI Events on changes (if above detecting for changes is enabled). 110 | 111 | Read more about [Config Events](https://github.com/microprofile-extensions/config-ext/blob/master/config-events/README.md) 112 | 113 | You can disable this with the `configsource.json.notifyOnChanges` property: 114 | 115 | configsource.json.notifyOnChanges=false 116 | 117 | If you added more than one resource as source, the event will only fire if the resulting file change also changed the global source change, as one file takes priority over the other. 118 | 119 | ### Key separator 120 | 121 | By default the separator used in the key is a DOT (.) example: 122 | 123 | ```property 124 | 125 | "location.protocol": "http" 126 | ``` 127 | 128 | You can change this by setting `configsource.json.keyseparator` to the desired separator, example: 129 | 130 | configsource.json.keyseparator=_ 131 | 132 | will create: 133 | 134 | ```property 135 | 136 | "location_protocol": "http" 137 | ``` 138 | -------------------------------------------------------------------------------- /configsource-json/application.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": { 3 | "property": "a-string-value" 4 | }, 5 | "deepList":{ 6 | "level1":[ 7 | "l1", 8 | "l2" 9 | ] 10 | }, 11 | "listTest":[ 12 | "item1", 13 | "item2", 14 | "item3,stillItem3" 15 | ] 16 | } -------------------------------------------------------------------------------- /configsource-json/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-json 12 | 13 | Microprofile Config Extensions :: Json config source 14 | A config that get the values from a json file 15 | 16 | 17 | 18 | ${project.groupId} 19 | configsource-filebase 20 | ${project.version} 21 | 22 | 23 | 24 | 25 | org.glassfish 26 | jakarta.json 27 | 2.0.1 28 | test 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /configsource-json/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.json.JsonConfigSource -------------------------------------------------------------------------------- /configsource-json/src/test/java/org/microprofileext/config/source/json/DisabledWhenEnabledKeyIsFalseTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.json; 2 | 3 | import java.util.NoSuchElementException; 4 | import jakarta.enterprise.inject.spi.Extension; 5 | import jakarta.inject.Inject; 6 | import org.eclipse.microprofile.config.Config; 7 | import org.eclipse.microprofile.config.spi.ConfigSource; 8 | import org.jboss.arquillian.container.test.api.Deployment; 9 | import org.jboss.arquillian.junit.Arquillian; 10 | import org.jboss.shrinkwrap.api.ShrinkWrap; 11 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author Phillip Kruger 17 | */ 18 | @RunWith(Arquillian.class) 19 | public class DisabledWhenEnabledKeyIsFalseTest { 20 | 21 | @Inject 22 | Config config; 23 | 24 | @Deployment 25 | public static JavaArchive createDeployment() { 26 | return ShrinkWrap.create(JavaArchive.class) 27 | .addPackages(true, Config.class.getPackage()) 28 | .addAsServiceProviderAndClasses(ConfigSource.class, JsonConfigSource.class) 29 | .addAsResource(DisabledWhenEnabledKeyIsFalseTest.class.getClassLoader().getResource("config-disabled.properties"), "META-INF/microprofile-config.properties") 30 | .addAsManifestResource("META-INF/beans.xml"); 31 | } 32 | 33 | @Test(expected = NoSuchElementException.class) 34 | public void testPropertyFailsWhenExplicitlyDisabled() { 35 | config.getValue("test.property", String.class); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /configsource-json/src/test/java/org/microprofileext/config/source/json/EnabledWhenEnabledKeyIsMissingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Derek P. Moore. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.microprofileext.config.source.json; 17 | 18 | import jakarta.enterprise.inject.spi.Extension; 19 | import jakarta.inject.Inject; 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | import org.eclipse.microprofile.config.Config; 22 | import org.eclipse.microprofile.config.spi.ConfigSource; 23 | import org.jboss.arquillian.container.test.api.Deployment; 24 | import org.jboss.arquillian.junit.Arquillian; 25 | import org.jboss.shrinkwrap.api.ShrinkWrap; 26 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | /** 31 | * @author Phillip Kruger 32 | */ 33 | @RunWith(Arquillian.class) 34 | public class EnabledWhenEnabledKeyIsMissingTest { 35 | 36 | @Inject 37 | Config config; 38 | 39 | @Deployment 40 | public static JavaArchive createDeployment() { 41 | return ShrinkWrap.create(JavaArchive.class) 42 | .addPackages(true, Config.class.getPackage()) 43 | .addAsServiceProviderAndClasses(ConfigSource.class, JsonConfigSource.class) 44 | .addAsResource(EnabledWhenEnabledKeyIsMissingTest.class.getClassLoader().getResource("config-empty.properties"), "META-INF/microprofile-config.properties") 45 | .addAsManifestResource("META-INF/beans.xml"); 46 | } 47 | 48 | @Test 49 | public void testPropertyLoadsWhenNotExplicitlyEnabled() { 50 | assertThat(config.getOptionalValue("test.property", String.class)).get() 51 | .isEqualTo("a-string-value") 52 | .as("test.property in application.properties is set to a-string-value"); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /configsource-json/src/test/java/org/microprofileext/config/source/json/EnabledWhenEnabledKeyIsTrueTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Derek P. Moore. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.microprofileext.config.source.json; 17 | 18 | import jakarta.enterprise.inject.spi.Extension; 19 | import jakarta.inject.Inject; 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | import org.eclipse.microprofile.config.Config; 22 | import org.eclipse.microprofile.config.spi.ConfigSource; 23 | import org.jboss.arquillian.container.test.api.Deployment; 24 | import org.jboss.arquillian.junit.Arquillian; 25 | import org.jboss.shrinkwrap.api.ShrinkWrap; 26 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | /** 31 | * @author Phillip Kruger 32 | */ 33 | @RunWith(Arquillian.class) 34 | public class EnabledWhenEnabledKeyIsTrueTest { 35 | 36 | @Inject 37 | Config config; 38 | 39 | @Deployment 40 | public static JavaArchive createDeployment() { 41 | return ShrinkWrap.create(JavaArchive.class) 42 | .addPackages(true, Config.class.getPackage()) 43 | .addAsServiceProviderAndClasses(ConfigSource.class, JsonConfigSource.class) 44 | .addAsResource(EnabledWhenEnabledKeyIsTrueTest.class.getClassLoader().getResource("config-enabled.properties"), "META-INF/microprofile-config.properties") 45 | .addAsManifestResource("META-INF/beans.xml"); 46 | } 47 | 48 | @Test 49 | public void testPropertyLoadsWhenExplicitlyEnabled() { 50 | assertThat(config.getOptionalValue("test.property", String.class)).get() 51 | .isEqualTo("a-string-value") 52 | .as("test.property in application.properties is set to a-string-value"); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /configsource-json/src/test/java/org/microprofileext/config/source/json/ListTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.json; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | import jakarta.enterprise.inject.spi.Extension; 6 | import jakarta.inject.Inject; 7 | import org.eclipse.microprofile.config.Config; 8 | import org.eclipse.microprofile.config.inject.ConfigProperty; 9 | import org.eclipse.microprofile.config.spi.ConfigSource; 10 | import org.jboss.arquillian.container.test.api.Deployment; 11 | import org.jboss.arquillian.junit.Arquillian; 12 | import org.jboss.shrinkwrap.api.ShrinkWrap; 13 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 14 | import org.junit.Assert; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | 18 | /** 19 | * @author Phillip Kruger 20 | */ 21 | @RunWith(Arquillian.class) 22 | public class ListTest { 23 | 24 | @Inject 25 | @ConfigProperty(name = "listTest") 26 | String stringList; 27 | 28 | @Inject 29 | @ConfigProperty(name = "listTest") 30 | List listList; 31 | 32 | @Inject 33 | @ConfigProperty(name = "listTest") 34 | Set setList; 35 | 36 | @Inject 37 | @ConfigProperty(name = "listTest") 38 | String[] arrayList; 39 | 40 | @Inject 41 | @ConfigProperty(name = "deepList.level1") 42 | List deepList; 43 | 44 | @Deployment 45 | public static JavaArchive createDeployment() { 46 | return ShrinkWrap.create(JavaArchive.class) 47 | .addPackages(true, Config.class.getPackage()) 48 | .addAsServiceProviderAndClasses(ConfigSource.class, JsonConfigSource.class) 49 | .addAsManifestResource("META-INF/beans.xml"); 50 | } 51 | 52 | @Test 53 | public void testStringList() { 54 | Assert.assertEquals("item1,item2,item3\\,stillItem3", stringList); 55 | } 56 | 57 | @Test 58 | public void testListList() { 59 | Assert.assertNotNull(listList); 60 | Assert.assertEquals(3, listList.size()); 61 | 62 | } 63 | 64 | @Test 65 | public void testSetList() { 66 | Assert.assertNotNull(setList); 67 | Assert.assertEquals(3, setList.size()); 68 | } 69 | 70 | @Test 71 | public void testArrayList() { 72 | Assert.assertNotNull(arrayList); 73 | Assert.assertEquals(3, arrayList.length); 74 | } 75 | 76 | @Test 77 | public void testDeepList(){ 78 | Assert.assertNotNull(deepList); 79 | Assert.assertEquals(2, deepList.size()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /configsource-json/src/test/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /configsource-json/src/test/resources/config-disabled.properties: -------------------------------------------------------------------------------- 1 | JsonConfigSource.enabled=false 2 | -------------------------------------------------------------------------------- /configsource-json/src/test/resources/config-empty.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /configsource-json/src/test/resources/config-enabled.properties: -------------------------------------------------------------------------------- 1 | JsonConfigSource.enabled=true 2 | -------------------------------------------------------------------------------- /configsource-memory/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Memory config source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-memory/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-memory) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-memory.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-memory) 7 | 8 | This source gets and sets values in memory. Useful when you want to change config during runtime. 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-memory 17 | XXXX 18 | 19 | 20 | ``` 21 | 22 | ## Info 23 | 24 | You can do this by using the REST API to change the config values: 25 | 26 | ``` 27 | 28 | GET /microprofile-ext/memoryconfigsource/sources - list all config sources 29 | GET /microprofile-ext/memoryconfigsource/all - get all configurations 30 | GET /microprofile-ext/memoryconfigsource/key/{key} - get the configured value for {key} 31 | PUT /microprofile-ext/memoryconfigsource/key/{key} - set the value for {key} 32 | DELETE /microprofile-ext/memoryconfigsource/key/{key} - delete the configured value for {key} 33 | 34 | ``` 35 | 36 | ### Curl Examples 37 | 38 | Add a property to `some.key` with value `some value`: 39 | 40 | ``` 41 | curl -X PUT "http://localhost:8080/config-example/api/microprofile-ext/memoryconfigsource/key/some.key" -H "accept: */*" -H "Content-Type: text/plain" -d "some value" 42 | ``` 43 | 44 | Get the property `some.key`: 45 | 46 | ``` 47 | curl -X GET "http://localhost:8080/config-example/api/microprofile-ext/memoryconfigsource/key/some.key" -H "accept: */*" 48 | ``` 49 | 50 | Get the property `some.key` but only at the `SystemProperty` source: 51 | 52 | ``` 53 | curl -X GET "http://localhost:8080/config-example/api/microprofile-ext/memoryconfigsource/key/some.key?configsource=SysPropConfigSource" -H "accept: */*" 54 | ``` 55 | 56 | Delete the property `some.key` 57 | 58 | ``` 59 | curl -X DELETE "http://localhost:8080/config-example/api/microprofile-ext/memoryconfigsource/key/some.key" -H "accept: */*" 60 | ``` 61 | 62 | ## Events 63 | 64 | This config source fires CDI Events on PUT and DELETE: 65 | 66 | Read more about [Config Events](https://github.com/microprofile-extensions/config-ext/blob/master/config-events/README.md) 67 | 68 | You can disable this with the `MemoryConfigSource.notifyOnChanges` property 69 | 70 | ## Configure options 71 | 72 | You can disable the config source by setting this config: 73 | 74 | MemoryConfigSource.enabled=false 75 | 76 | You can disable the change notification eventson changes by setting this config: 77 | 78 | MemoryConfigSource.notifyOnChanges=false 79 | 80 | ![REST API](https://github.com/microprofile-extensions/config-ext/raw/master/configsource-memory/screenshot.png) 81 | -------------------------------------------------------------------------------- /configsource-memory/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-memory 12 | 13 | Microprofile Config Extensions :: Memory config source 14 | A config that get the values from memory 15 | 16 | 17 | 18 | ${project.groupId} 19 | configsource-base 20 | ${project.version} 21 | 22 | 23 | ${project.groupId} 24 | config-events 25 | ${project.version} 26 | 27 | 28 | ${project.groupId} 29 | configsource-providers 30 | ${project.version} 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /configsource-memory/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microprofile-extensions/config-ext/c1487f15f9d4246a9cd655ca72c5b43b8196dfa1/configsource-memory/screenshot.png -------------------------------------------------------------------------------- /configsource-memory/src/main/java/org/microprofileext/config/source/memory/MemoryConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.memory; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.logging.Logger; 6 | import org.microprofileext.config.source.base.EnabledConfigSource; 7 | 8 | /** 9 | * In memory config source. Use the REST Endpoint to populate values 10 | * @author Phillip Kruger 11 | */ 12 | public class MemoryConfigSource extends EnabledConfigSource { 13 | 14 | private static final Logger log = Logger.getLogger(MemoryConfigSource.class.getName()); 15 | 16 | public static final String NAME = "MemoryConfigSource"; 17 | private static final Map PROPERTIES = new HashMap<>(); 18 | 19 | public MemoryConfigSource(){ 20 | log.info("Loading [memory] MicroProfile ConfigSource"); 21 | super.initOrdinal(900); 22 | } 23 | 24 | @Override 25 | public Map getPropertiesIfEnabled() { 26 | return PROPERTIES; 27 | } 28 | 29 | @Override 30 | public String getValue(String key) { 31 | if(PROPERTIES.containsKey(key)){ 32 | return PROPERTIES.get(key); 33 | } 34 | return null; 35 | } 36 | 37 | @Override 38 | public String getName() { 39 | return NAME; 40 | } 41 | } -------------------------------------------------------------------------------- /configsource-memory/src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /configsource-memory/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.memory.MemoryConfigSource -------------------------------------------------------------------------------- /configsource-properties/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Properties Config Source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-properties/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-properties) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-properties.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-properties) 7 | 8 | This source gets values from some properties file(s). 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-properties 17 | XXXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Example: 24 | 25 | ```properties 26 | 27 | somekey=somevalue 28 | location.protocol=http 29 | location.host=localhost 30 | location.port=8080 31 | location.path=/some/path 32 | location.jedis=Yoda, Qui-Gon Jinn, Obi-Wan Kenobi, Luke Skywalker 33 | 34 | ``` 35 | 36 | 37 | You can `inject` the jedis using any of the following: 38 | 39 | ```java 40 | 41 | @Inject 42 | @ConfigProperty(name = "location.jedis") 43 | String jedisAsString; 44 | 45 | @Inject 46 | @ConfigProperty(name = "location.jedis") 47 | List jedisAsList; 48 | 49 | @Inject 50 | @ConfigProperty(name = "location.jedis") 51 | Set jedisAsSet; 52 | 53 | @Inject 54 | @ConfigProperty(name = "location.jedis") 55 | String[] jedisAsArray; 56 | 57 | ``` 58 | 59 | ## Configure options 60 | 61 | ### Url(s) 62 | 63 | By default the config source will look for a file called `application.properties`. You can set the location(s) of the files: 64 | 65 | configsource.properties.url= 66 | 67 | example: 68 | 69 | configsource.properties.url=file:/tmp/myconfig.properties 70 | 71 | You can also add more than one location by comma-separating the location: 72 | 73 | configsource.properties.url=file:/tmp/myconfig.properties,http://localhost/myconfig.properties 74 | 75 | The latest files will override properties in previous files. As example, if using above configuration, property `foo=bar` in `file:/tmp/myconfig.properties` will be override if it's added to `http://localhost/myconfig.properties`. 76 | 77 | ### Detecting changes. 78 | 79 | You can watch the resource for changes. This feature is disabled by default. To enable: 80 | 81 | configsource.properties.pollForChanges=true 82 | 83 | By default it will poll every **5 seconds**. You can change that, example to poll every 5 minutes: 84 | 85 | configsource.properties.pollInterval=300 86 | 87 | ### Events 88 | 89 | This config source fires CDI Events on changes (if above detecting for changes is enabled). 90 | 91 | Read more about [Config Events](https://github.com/microprofile-extensions/config-ext/blob/master/config-events/README.md) 92 | 93 | You can disable this with the `configsource.properties.notifyOnChanges` property: 94 | 95 | configsource.properties.notifyOnChanges=false 96 | 97 | If you added more than one resource as source, the event will only fire if the resulting file change also changed the global source change, as one file takes priority over the other. 98 | 99 | ### Key separator 100 | 101 | By default the separator used in the key is a DOT (.) example: 102 | 103 | ```property 104 | 105 | "location.protocol": "http" 106 | ``` 107 | 108 | You can change this by setting `configsource.properties.keyseparator` to the desired separator, example: 109 | 110 | configsource.properties.keyseparator=_ 111 | 112 | will create: 113 | 114 | ```property 115 | 116 | "location_protocol": "http" 117 | ``` 118 | -------------------------------------------------------------------------------- /configsource-properties/application.properties: -------------------------------------------------------------------------------- 1 | test.property=a-string-value 2 | deepList.level1=l1,l2 3 | listTest=item1,item2,item3\\,stillItem3 -------------------------------------------------------------------------------- /configsource-properties/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-properties 12 | 13 | Microprofile Config Extensions :: Properties config source 14 | A config that gets its values from a properties file(s) at a URL 15 | 16 | 17 | 18 | ${project.groupId} 19 | configsource-filebase 20 | ${project.version} 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /configsource-properties/src/main/java/org/microprofileext/config/source/properties/PropertiesConfigSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.microprofileext.config.source.properties; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.Map; 22 | import java.util.Properties; 23 | import java.util.Set; 24 | import java.util.logging.Level; 25 | import java.util.logging.Logger; 26 | import org.microprofileext.config.source.base.file.AbstractUrlBasedSource; 27 | 28 | /** 29 | * Properties config source 30 | * @author Mark Struberg 31 | * @author Derek P. Moore 32 | * @author Phillip Kruger 33 | */ 34 | public class PropertiesConfigSource extends AbstractUrlBasedSource { 35 | 36 | private static final Logger log = Logger.getLogger(PropertiesConfigSource.class.getName()); 37 | 38 | @Override 39 | protected String getFileExtension() { 40 | return "properties"; 41 | } 42 | 43 | @Override @SuppressWarnings("unchecked") 44 | protected Map toMap(InputStream inputStream) { 45 | Properties props = new Properties(); 46 | try { 47 | props.load(inputStream); 48 | if(!super.getKeySeparator().equalsIgnoreCase(DOT))props = changeKeySeparator(props); 49 | } catch (IOException e) { 50 | log.log(Level.WARNING, "Unable to load properties [{0}]", e.getMessage()); 51 | } 52 | return (Map) props; 53 | } 54 | 55 | private Properties changeKeySeparator(Properties props){ 56 | Properties nprops = new Properties(); 57 | Set keySet = props.keySet(); 58 | for(Object k:keySet){ 59 | String key = (String)k; 60 | String value = props.getProperty(key); 61 | key = key.replaceAll(WHACK_WHACK + DOT, super.getKeySeparator()); 62 | nprops.put(key, value); 63 | } 64 | 65 | return nprops; 66 | } 67 | 68 | private static final String DOT = "."; 69 | private static final String WHACK_WHACK = "\\"; 70 | } 71 | -------------------------------------------------------------------------------- /configsource-properties/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.properties.PropertiesConfigSource -------------------------------------------------------------------------------- /configsource-properties/src/test/java/org/microprofileext/config/source/properties/DisabledWhenEnabledKeyIsFalseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Derek P. Moore. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.microprofileext.config.source.properties; 17 | 18 | import java.util.NoSuchElementException; 19 | import jakarta.inject.Inject; 20 | import org.eclipse.microprofile.config.Config; 21 | import org.eclipse.microprofile.config.spi.ConfigSource; 22 | import org.jboss.arquillian.container.test.api.Deployment; 23 | import org.jboss.arquillian.junit.Arquillian; 24 | import org.jboss.shrinkwrap.api.ShrinkWrap; 25 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 26 | import org.junit.Test; 27 | import org.junit.runner.RunWith; 28 | 29 | /** 30 | * @author Derek P. Moore 31 | */ 32 | @RunWith(Arquillian.class) 33 | public class DisabledWhenEnabledKeyIsFalseTest { 34 | 35 | @Inject 36 | Config config; 37 | 38 | @Deployment 39 | public static JavaArchive createDeployment() { 40 | return ShrinkWrap.create(JavaArchive.class) 41 | .addPackages(true, Config.class.getPackage()) 42 | .addAsServiceProviderAndClasses(ConfigSource.class, PropertiesConfigSource.class) 43 | .addAsResource(DisabledWhenEnabledKeyIsFalseTest.class.getClassLoader().getResource("config-disabled.properties"), "META-INF/microprofile-config.properties") 44 | .addAsManifestResource("META-INF/beans.xml"); 45 | } 46 | 47 | @Test(expected = NoSuchElementException.class) 48 | public void testPropertyFailsWhenExplicitlyDisabled() { 49 | config.getValue("test.property", String.class); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /configsource-properties/src/test/java/org/microprofileext/config/source/properties/EnabledWhenEnabledKeyIsMissingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Derek P. Moore. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.microprofileext.config.source.properties; 17 | 18 | import jakarta.inject.Inject; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import org.eclipse.microprofile.config.Config; 22 | import org.eclipse.microprofile.config.spi.ConfigSource; 23 | import org.jboss.arquillian.container.test.api.Deployment; 24 | import org.jboss.arquillian.junit.Arquillian; 25 | import org.jboss.shrinkwrap.api.ShrinkWrap; 26 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | /** 31 | * @author Derek P. Moore 32 | */ 33 | @RunWith(Arquillian.class) 34 | public class EnabledWhenEnabledKeyIsMissingTest { 35 | 36 | @Inject 37 | Config config; 38 | 39 | @Deployment 40 | public static JavaArchive createDeployment() { 41 | return ShrinkWrap.create(JavaArchive.class) 42 | .addPackages(true, Config.class.getPackage()) 43 | .addAsServiceProviderAndClasses(ConfigSource.class, PropertiesConfigSource.class) 44 | .addAsResource(EnabledWhenEnabledKeyIsMissingTest.class.getClassLoader().getResource("config-empty.properties"), "META-INF/microprofile-config.properties") 45 | .addAsManifestResource("META-INF/beans.xml"); 46 | } 47 | 48 | @Test 49 | public void testPropertyLoadsWhenNotExplicitlyEnabled() { 50 | assertThat(config.getOptionalValue("test.property", String.class)).get() 51 | .isEqualTo("a-string-value") 52 | .as("test.property in application.properties is set to a-string-value"); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /configsource-properties/src/test/java/org/microprofileext/config/source/properties/EnabledWhenEnabledKeyIsTrueTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Derek P. Moore. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.microprofileext.config.source.properties; 17 | 18 | import jakarta.inject.Inject; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import org.eclipse.microprofile.config.Config; 21 | import org.eclipse.microprofile.config.spi.ConfigSource; 22 | import org.jboss.arquillian.container.test.api.Deployment; 23 | import org.jboss.arquillian.junit.Arquillian; 24 | import org.jboss.shrinkwrap.api.ShrinkWrap; 25 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 26 | import org.junit.Test; 27 | import org.junit.runner.RunWith; 28 | 29 | /** 30 | * @author Derek P. Moore 31 | */ 32 | @RunWith(Arquillian.class) 33 | public class EnabledWhenEnabledKeyIsTrueTest { 34 | 35 | @Inject 36 | Config config; 37 | 38 | @Deployment 39 | public static JavaArchive createDeployment() { 40 | return ShrinkWrap.create(JavaArchive.class) 41 | .addPackages(true, Config.class.getPackage()) 42 | .addAsServiceProviderAndClasses(ConfigSource.class, PropertiesConfigSource.class) 43 | .addAsResource(EnabledWhenEnabledKeyIsTrueTest.class.getClassLoader().getResource("config-enabled.properties"), "META-INF/microprofile-config.properties") 44 | .addAsManifestResource("META-INF/beans.xml"); 45 | } 46 | 47 | @Test 48 | public void testPropertyLoadsWhenExplicitlyEnabled() { 49 | assertThat(config.getOptionalValue("test.property", String.class)).get() 50 | .isEqualTo("a-string-value") 51 | .as("test.property in application.properties is set to a-string-value"); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /configsource-properties/src/test/java/org/microprofileext/config/source/properties/ListTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.properties; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | import jakarta.enterprise.inject.spi.Extension; 6 | import jakarta.inject.Inject; 7 | import org.eclipse.microprofile.config.Config; 8 | import org.eclipse.microprofile.config.inject.ConfigProperty; 9 | import org.eclipse.microprofile.config.spi.ConfigSource; 10 | import org.jboss.arquillian.container.test.api.Deployment; 11 | import org.jboss.arquillian.junit.Arquillian; 12 | import org.jboss.shrinkwrap.api.ShrinkWrap; 13 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 14 | import org.junit.Assert; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | 18 | /** 19 | * @author Phillip Kruger 20 | */ 21 | @RunWith(Arquillian.class) 22 | public class ListTest { 23 | 24 | @Inject 25 | @ConfigProperty(name = "listTest") 26 | String stringList; 27 | 28 | @Inject 29 | @ConfigProperty(name = "listTest") 30 | List listList; 31 | 32 | @Inject 33 | @ConfigProperty(name = "listTest") 34 | Set setList; 35 | 36 | @Inject 37 | @ConfigProperty(name = "listTest") 38 | String[] arrayList; 39 | 40 | @Inject 41 | @ConfigProperty(name = "deepList.level1") 42 | List deepList; 43 | 44 | @Deployment 45 | public static JavaArchive createDeployment() { 46 | return ShrinkWrap.create(JavaArchive.class) 47 | .addPackages(true, Config.class.getPackage()) 48 | .addAsServiceProviderAndClasses(ConfigSource.class, PropertiesConfigSource.class) 49 | .addAsManifestResource("META-INF/beans.xml"); 50 | } 51 | 52 | @Test 53 | public void testStringList() { 54 | Assert.assertEquals("item1,item2,item3\\,stillItem3", stringList); 55 | } 56 | 57 | @Test 58 | public void testListList() { 59 | Assert.assertNotNull(listList); 60 | Assert.assertEquals(3, listList.size()); 61 | 62 | } 63 | 64 | @Test 65 | public void testSetList() { 66 | Assert.assertNotNull(setList); 67 | Assert.assertEquals(3, setList.size()); 68 | } 69 | 70 | @Test 71 | public void testArrayList() { 72 | Assert.assertNotNull(arrayList); 73 | Assert.assertEquals(3, arrayList.length); 74 | } 75 | 76 | @Test 77 | public void testDeepList(){ 78 | Assert.assertNotNull(deepList); 79 | Assert.assertEquals(2, deepList.size()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /configsource-properties/src/test/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /configsource-properties/src/test/resources/config-disabled.properties: -------------------------------------------------------------------------------- 1 | PropertiesConfigSource.enabled=false 2 | -------------------------------------------------------------------------------- /configsource-properties/src/test/resources/config-empty.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /configsource-properties/src/test/resources/config-enabled.properties: -------------------------------------------------------------------------------- 1 | PropertiesConfigSource.enabled=true 2 | -------------------------------------------------------------------------------- /configsource-providers/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Configsource providers 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-providers/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-providers) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-providers.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-providers) 7 | 8 | Util library that makes all Configsources available via CDI 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-providers 17 | XXXX 18 | 19 | 20 | ``` 21 | 22 | ## Injecting sources 23 | 24 | You can inject a certain ConfigSource by referencing it by name: 25 | 26 | ```java 27 | 28 | @Inject @Name("MemoryConfigSource") 29 | private ConfigSource memoryConfigSource; 30 | 31 | @Inject @Name("SysPropConfigSource") 32 | private ConfigSource systemPropertiesConfigSource; 33 | 34 | ``` 35 | 36 | You can also get a Map of all config sources, with the key in the map the name of the source and the value the source: 37 | 38 | ```java 39 | 40 | @Inject @ConfigSourceMap 41 | private Map configSourceMap; 42 | 43 | ``` 44 | -------------------------------------------------------------------------------- /configsource-providers/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-providers 12 | 13 | Microprofile Config Extensions :: Configsource providers 14 | A library that makes the config sources available via CDI 15 | 16 | -------------------------------------------------------------------------------- /configsource-providers/src/main/java/org/microprofileext/config/cdi/ConfigSourceMap.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.cdi; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import jakarta.inject.Qualifier; 8 | 9 | /** 10 | * Mark a map that contains the configsources 11 | * @author Phillip Kruger 12 | */ 13 | @Qualifier 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.METHOD, ElementType.FIELD}) 16 | public @interface ConfigSourceMap { 17 | } -------------------------------------------------------------------------------- /configsource-providers/src/main/java/org/microprofileext/config/cdi/ConfigSourceMapProvider.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.cdi; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.logging.Logger; 6 | import jakarta.enterprise.context.ApplicationScoped; 7 | import jakarta.enterprise.inject.Produces; 8 | import jakarta.inject.Inject; 9 | import jakarta.inject.Provider; 10 | import org.eclipse.microprofile.config.Config; 11 | import org.eclipse.microprofile.config.spi.ConfigSource; 12 | 13 | /** 14 | * Making the Config sources available via CDI 15 | * @author Phillip Kruger 16 | */ 17 | @ApplicationScoped 18 | public class ConfigSourceMapProvider { 19 | 20 | private static final Logger log = Logger.getLogger(ConfigSourceMapProvider.class.getName()); 21 | 22 | @Inject 23 | private Provider configProvider; 24 | 25 | private final Map configSourceMap = new HashMap<>(); 26 | 27 | @Produces @ConfigSourceMap 28 | public Map produceConfigSourceMap(){ 29 | if(this.configSourceMap.isEmpty()){ 30 | for(ConfigSource configSource:configProvider.get().getConfigSources()){ 31 | this.configSourceMap.put(configSource.getName(), configSource); 32 | } 33 | } 34 | return this.configSourceMap; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /configsource-providers/src/main/java/org/microprofileext/config/cdi/ConfigSourceProvider.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.cdi; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import java.util.logging.Logger; 7 | import jakarta.enterprise.context.Dependent; 8 | import jakarta.enterprise.inject.Produces; 9 | import jakarta.enterprise.inject.spi.InjectionPoint; 10 | import jakarta.inject.Inject; 11 | import org.eclipse.microprofile.config.spi.ConfigSource; 12 | 13 | /** 14 | * Making the Config sources available via CDI 15 | * @author Phillip Kruger 16 | */ 17 | @Dependent 18 | public class ConfigSourceProvider { 19 | 20 | private static final Logger log = Logger.getLogger(ConfigSourceProvider.class.getName()); 21 | 22 | @Inject @ConfigSourceMap 23 | private Map configSourceMap; 24 | 25 | @Produces @Name("") 26 | public ConfigSource produceConfigSource(final InjectionPoint injectionPoint) { 27 | Set qualifiers = injectionPoint.getQualifiers(); 28 | String name = getName(qualifiers); 29 | return configSourceMap.get(name); 30 | } 31 | 32 | private String getName(Set qualifiers){ 33 | for(Annotation qualifier:qualifiers){ 34 | if(qualifier.annotationType().equals(Name.class)){ 35 | Name name = (Name)qualifier; 36 | return name.value(); 37 | } 38 | } 39 | return ""; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /configsource-providers/src/main/java/org/microprofileext/config/cdi/Name.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.cdi; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import jakarta.enterprise.util.Nonbinding; 8 | import jakarta.inject.Qualifier; 9 | 10 | /** 11 | * The define the name of a config source 12 | * @author Phillip Kruger 13 | */ 14 | @Qualifier 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target({ElementType.METHOD, ElementType.FIELD}) 17 | public @interface Name { 18 | @Nonbinding String value(); 19 | } -------------------------------------------------------------------------------- /configsource-providers/src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /configsource-typesafeconfig/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # TypeSafeConfig Config Source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-typesafeconfig/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-consul) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-typesafeconfig.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-consul) 7 | 8 | Use [TypeSafe Config](https://github.com/lightbend/config) to get config values. 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-typesafeconfig 17 | XXXXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Configure options 24 | 25 | You can disable the config source by setting this config: 26 | 27 | TypeSafeConfigConfigSource.enabled=false 28 | 29 | ## Links 30 | * https://microprofile.io/project/eclipse/microprofile-config 31 | -------------------------------------------------------------------------------- /configsource-typesafeconfig/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-typesafeconfig 12 | 13 | Microprofile Config Extensions :: TypeSafe Config config source 14 | A config that gets the values from a TypeSafe Config configuration source 15 | 16 | 17 | 5.9.3 18 | 1.9.3 19 | 20 | 21 | 22 | 23 | ${project.groupId} 24 | configsource-base 25 | ${project.version} 26 | 27 | 28 | 29 | com.typesafe 30 | config 31 | 1.4.2 32 | 33 | 34 | 35 | org.junit.platform 36 | junit-platform-runner 37 | ${junit.platform.version} 38 | test 39 | 40 | 41 | org.junit.jupiter 42 | junit-jupiter-engine 43 | ${junit.jupiter.version} 44 | test 45 | 46 | 47 | org.mockito 48 | mockito-core 49 | 5.3.1 50 | test 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /configsource-typesafeconfig/src/main/java/org/microprofileext/config/source/typesafeconfig/TypeSafeConfigConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.typesafeconfig; 2 | 3 | import com.typesafe.config.Config; 4 | import com.typesafe.config.ConfigException; 5 | import com.typesafe.config.ConfigFactory; 6 | import org.eclipse.microprofile.config.spi.ConfigSource; 7 | import org.microprofileext.config.source.base.EnabledConfigSource; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import java.util.logging.Level; 13 | import java.util.logging.Logger; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * MicroProfile Config {@link ConfigSource} which is backed by the TypeSafe Config configuration 18 | * capabilities. By default, unless a explicit config is provided this will leverage the default 19 | * TypeSafe config instance. 20 | */ 21 | public class TypeSafeConfigConfigSource extends EnabledConfigSource { 22 | 23 | private static final Logger log = Logger.getLogger(TypeSafeConfigConfigSource.class.getName()); 24 | 25 | private static final String NAME = "TypeSafeConfigConfigSource"; 26 | private Config config; 27 | 28 | /** 29 | * Create a Config Source that is backed by the default TypeSafe config via {@link ConfigFactory#load()}. 30 | */ 31 | public TypeSafeConfigConfigSource() { 32 | this(ConfigFactory.load()); 33 | } 34 | 35 | /** 36 | * Create a Config Source that is backed by a provided TypeSafe config. 37 | * @param config The configuration to use as the backing configuration. Any key requested will be traversed from 38 | * the root of this config. 39 | */ 40 | public TypeSafeConfigConfigSource(Config config) { 41 | super(); 42 | this.config = config; 43 | this.initOrdinal(310); 44 | } 45 | 46 | @Override 47 | public Set getPropertyNames() { 48 | // Not sure how often this is called if we need to do any sort of caching or not. 49 | return config.entrySet() 50 | .stream() 51 | .map(Map.Entry::getKey) 52 | .collect(Collectors.toSet()); 53 | } 54 | 55 | @Override 56 | public Map getPropertiesIfEnabled() { 57 | HashMap props = new HashMap<>(); 58 | config.entrySet() 59 | .forEach(entry -> 60 | props.put(entry.getKey(), config.getString(entry.getKey()))); 61 | return props; 62 | } 63 | 64 | @Override 65 | public String getValue(String key) { 66 | try { 67 | log.log(Level.FINE, "load {0} from typesafe config", key); 68 | return config.getString(key); 69 | } catch (ConfigException ex) { 70 | log.log(Level.WARNING, "Config key ''{0}'' could not be retrieved due to ConfigException with " 71 | + "message ''{1}''. To see stack trace enable trace logging", new String[]{key, ex.getMessage()}); 72 | log.log(Level.FINER, "ConfigException stack trace", ex); 73 | return null; 74 | } 75 | } 76 | 77 | @Override 78 | public String getName() { 79 | return NAME; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /configsource-typesafeconfig/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.typesafeconfig.TypeSafeConfigConfigSource -------------------------------------------------------------------------------- /configsource-typesafeconfig/src/test/java/org/microprofileext/config/source/typesafeconfig/TypeSafeConfigConfigSourceTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.typesafeconfig; 2 | 3 | import com.typesafe.config.Config; 4 | import com.typesafe.config.ConfigFactory; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | import static org.mockito.Mockito.mock; 10 | 11 | class TypeSafeConfigConfigSourceTest { 12 | 13 | private TypeSafeConfigConfigSource configSource; 14 | private Config testConfig; 15 | 16 | @BeforeEach 17 | public void init() { 18 | testConfig = ConfigFactory.parseResources( 19 | TypeSafeConfigConfigSourceTest.class.getClassLoader(), 20 | "TestConfig.conf"); 21 | configSource = new TypeSafeConfigConfigSource(testConfig); 22 | } 23 | 24 | @Test 25 | void testGetProperties_empty() { 26 | TypeSafeConfigConfigSource emptyConfigSource = new TypeSafeConfigConfigSource(mock(Config.class)); 27 | assertTrue(emptyConfigSource.getProperties().isEmpty()); 28 | } 29 | 30 | @Test 31 | void testGetProperties_one() { 32 | // As properties are added to TestConfig.conf this will need to be incremented to match 33 | int NUM_EXPECTED_PROPERTIES = 2; 34 | assertEquals(NUM_EXPECTED_PROPERTIES, configSource.getProperties().size()); 35 | } 36 | 37 | @Test 38 | void testGetValue_null() { 39 | assertNull(configSource.getValue("thisKeyDoestNotExist")); 40 | } 41 | 42 | @Test 43 | void testGetValue() { 44 | assertEquals("hello", configSource.getValue("test")); 45 | } 46 | 47 | 48 | @Test 49 | void testGetValue_exception() { 50 | // Retrieving a value that is not stringable from the configuration will result in an exception from TypeSafe 51 | // config which should return null through this interface. 52 | assertNull(configSource.getValue("config_object")); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /configsource-typesafeconfig/src/test/resources/TestConfig.conf: -------------------------------------------------------------------------------- 1 | test = "hello" 2 | 3 | config_object { 4 | some_value = "" 5 | } -------------------------------------------------------------------------------- /configsource-xml/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Xml Config Source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-xml/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-xml) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-xml.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-xml) 7 | 8 | This source gets values from some xml file(s). 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-xml 17 | XXXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Example: 24 | 25 | ```xml 26 | 27 | 28 | 29 | somevalue 30 | 31 | http 32 | localhost 33 | 8080 34 | /some/path 35 | Yoda 36 | Qui-Gon Jinn 37 | Obi-Wan Kenobi 38 | Luke Skywalker 39 | 40 | 41 | ``` 42 | 43 | will create the following properties: 44 | 45 | ```property 46 | 47 | "somekey": "somevalue" 48 | "location.protocol": "http" 49 | "location.host": "localhost" 50 | "location.port": "8080" 51 | "location.path": "/some/path" 52 | "location.jedis": "Yoda, Qui-Gon Jinn, Obi-Wan Kenobi, Luke Skywalker" 53 | 54 | ``` 55 | 56 | 57 | You can `inject` the jedis using any of the following: 58 | 59 | ```java 60 | 61 | @Inject 62 | @ConfigProperty(name = "location.jedis") 63 | String jedisAsString; 64 | 65 | @Inject 66 | @ConfigProperty(name = "location.jedis") 67 | List jedisAsList; 68 | 69 | @Inject 70 | @ConfigProperty(name = "location.jedis") 71 | Set jedisAsSet; 72 | 73 | @Inject 74 | @ConfigProperty(name = "location.jedis") 75 | String[] jedisAsArray; 76 | 77 | ``` 78 | 79 | ## Configure options 80 | 81 | ### Url(s) 82 | 83 | By default the config source will look for a file called `application.xml`. You can set the location(s) of the files: 84 | 85 | configsource.xml.url= 86 | 87 | example: 88 | 89 | configsource.xml.url=file:/tmp/myconfig.xml 90 | 91 | You can also add more than one location by comma-separating the location: 92 | 93 | configsource.xml.url=file:/tmp/myconfig.xml,http://localhost/myconfig.xml 94 | 95 | The latest files will override properties in previous files. As example, if using above configuration, property `foo=bar` in `file:/tmp/myconfig.xml` will be override if it's added to `http://localhost/myconfig.xml`. 96 | 97 | ### Detecting changes. 98 | 99 | You can watch the resource for changes. This feature is disabled by default. To enable: 100 | 101 | configsource.xml.pollForChanges=true 102 | 103 | By default it will poll every **5 seconds**. You can change that, example to poll every 5 minutes: 104 | 105 | configsource.xml.pollInterval=300 106 | 107 | ### Events 108 | 109 | This config source fires CDI Events on changes (if above detecting for changes is enabled). 110 | 111 | Read more about [Config Events](https://github.com/microprofile-extensions/config-ext/blob/master/config-events/README.md) 112 | 113 | You can disable this with the `configsource.xml.notifyOnChanges` property: 114 | 115 | configsource.xml.notifyOnChanges=false 116 | 117 | If you added more than one resource as source, the event will only fire if the resulting file change also changed the global source change, as one file takes priority over the other. 118 | 119 | ### Key separator 120 | 121 | By default the separator used in the key is a DOT (.) example: 122 | 123 | ```property 124 | 125 | "location.protocol": "http" 126 | ``` 127 | 128 | You can change this by setting `configsource.xml.keyseparator` to the desired separator, example: 129 | 130 | configsource.xml.keyseparator=_ 131 | 132 | will create: 133 | 134 | ```property 135 | 136 | "location_protocol": "http" 137 | ``` 138 | ### Include root 139 | 140 | By default the root element of the XML is ignored. You can include the root by setting this property: 141 | 142 | configsource.xml.ignoreRoot=false 143 | 144 | Using the example above, this will create the following properties: 145 | 146 | ```property 147 | 148 | "root.somekey": "somevalue" 149 | "root.location.protocol": "http" 150 | "root.location.host": "localhost" 151 | "root.location.port": "8080" 152 | "root.location.path": "/some/path" 153 | "root.location.jedis": "[Yoda, Qui-Gon Jinn, Obi-Wan Kenobi, Luke Skywalker]" 154 | -------------------------------------------------------------------------------- /configsource-xml/application.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | a-string-value 5 | 6 | 7 | l1 8 | l2 9 | 10 | 11 | item1 12 | item2 13 | item3,stillItem3 14 | 15 | -------------------------------------------------------------------------------- /configsource-xml/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-xml 12 | 13 | Microprofile Config Extensions :: Xml config source 14 | A config that get the values from an xml file 15 | 16 | 17 | 18 | ${project.groupId} 19 | configsource-filebase 20 | ${project.version} 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /configsource-xml/src/main/java/org/microprofileext/config/source/xml/XmlConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.xml; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.logging.Level; 12 | import java.util.logging.Logger; 13 | import javax.xml.parsers.ParserConfigurationException; 14 | import javax.xml.parsers.SAXParserFactory; 15 | import org.eclipse.microprofile.config.Config; 16 | import org.microprofileext.config.source.base.file.AbstractUrlBasedSource; 17 | import org.xml.sax.Attributes; 18 | import org.xml.sax.InputSource; 19 | import org.xml.sax.SAXException; 20 | import org.xml.sax.helpers.DefaultHandler; 21 | 22 | /** 23 | * Xml config source 24 | * @author Phillip Kruger 25 | */ 26 | public class XmlConfigSource extends AbstractUrlBasedSource { 27 | 28 | private static final Logger log = Logger.getLogger(XmlConfigSource.class.getName()); 29 | 30 | @Override 31 | protected String getFileExtension() { 32 | return "xml"; 33 | } 34 | 35 | @Override 36 | protected Map toMap(final InputStream inputStream){ 37 | try { 38 | InputSource inputSource = new InputSource(inputStream); 39 | return parse(inputSource); 40 | } catch (SAXException | IOException | ParserConfigurationException ex) { 41 | log.log(Level.WARNING, "Could not create properties from XML [{0}]", ex.getMessage()); 42 | return new HashMap<>(); 43 | } 44 | } 45 | 46 | private Map parse(InputSource inputSource) throws SAXException, IOException, ParserConfigurationException { 47 | final Handler handler = new Handler(getConfig(),super.getKeySeparator()); 48 | SAXParserFactory.newInstance().newSAXParser().parse(inputSource, handler); 49 | return handler.result; 50 | } 51 | 52 | private class Handler extends DefaultHandler { 53 | private final StringBuilder valuebuffer = new StringBuilder(); 54 | private final List keybuffer = new LinkedList<>(); 55 | private final Map result = new HashMap<>(); 56 | 57 | private final boolean ignoreRoot; 58 | private final String keySeparator; 59 | private int depth = -1; 60 | 61 | public Handler(Config cfg,String keySeparator){ 62 | this.ignoreRoot = cfg.getOptionalValue("configsource.xml.ignoreRoot", Boolean.class).orElse(true); 63 | this.keySeparator = keySeparator; 64 | } 65 | 66 | @Override 67 | public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 68 | depth++; 69 | if (this.depth == 0 && ignoreRoot) { 70 | // Ignoring root 71 | }else{ 72 | keybuffer.add(qName); 73 | } 74 | } 75 | 76 | @Override 77 | public void endElement(String uri, String localName, String qName) throws SAXException { 78 | final String value = valuebuffer.toString().trim(); 79 | 80 | String key = String.join(keySeparator, keybuffer); 81 | if (!value.isEmpty()){ 82 | if(result.containsKey(key)){ 83 | result.put(key, addToList(result.get(key),value)); 84 | }else{ 85 | result.put(key, value.trim()); 86 | } 87 | } 88 | valuebuffer.setLength(0); 89 | keybuffer.remove(qName); 90 | depth--; 91 | } 92 | 93 | @Override 94 | public void characters(char[] ch, int start, int length) throws SAXException { 95 | valuebuffer.append(ch, start, length); 96 | } 97 | } 98 | 99 | private String addToList(String existing,String newElement){ 100 | if(newElement.contains(COMMA))newElement = newElement.replaceAll(COMMA, "\\\\,"); // Escape comma 101 | 102 | String[] split = existing.split(COMMA); 103 | List l = new ArrayList<>(Arrays.asList(split)); 104 | l.add(newElement); 105 | 106 | String join = String.join(COMMA, l); 107 | return join; 108 | } 109 | 110 | private static final String COMMA = ","; 111 | } -------------------------------------------------------------------------------- /configsource-xml/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.xml.XmlConfigSource -------------------------------------------------------------------------------- /configsource-xml/src/test/java/org/microprofileext/config/source/xml/DisabledWhenEnabledKeyIsFalseTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.xml; 2 | 3 | import java.util.NoSuchElementException; 4 | import jakarta.enterprise.inject.spi.Extension; 5 | import jakarta.inject.Inject; 6 | import org.eclipse.microprofile.config.Config; 7 | import org.eclipse.microprofile.config.spi.ConfigSource; 8 | import org.jboss.arquillian.container.test.api.Deployment; 9 | import org.jboss.arquillian.junit.Arquillian; 10 | import org.jboss.shrinkwrap.api.ShrinkWrap; 11 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | @author Phillip Kruger 17 | */ 18 | @RunWith(Arquillian.class) 19 | public class DisabledWhenEnabledKeyIsFalseTest { 20 | 21 | @Inject 22 | Config config; 23 | 24 | @Deployment 25 | public static JavaArchive createDeployment() { 26 | return ShrinkWrap.create(JavaArchive.class) 27 | .addPackages(true, Config.class.getPackage()) 28 | .addAsServiceProviderAndClasses(ConfigSource.class, XmlConfigSource.class) 29 | .addAsResource(DisabledWhenEnabledKeyIsFalseTest.class.getClassLoader().getResource("config-disabled.properties"), "META-INF/microprofile-config.properties") 30 | .addAsManifestResource("META-INF/beans.xml"); 31 | } 32 | 33 | @Test(expected = NoSuchElementException.class) 34 | public void testPropertyFailsWhenExplicitlyDisabled() { 35 | config.getValue("test.property", String.class); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /configsource-xml/src/test/java/org/microprofileext/config/source/xml/EnabledWhenEnabledKeyIsMissingTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.xml; 2 | 3 | import jakarta.enterprise.inject.spi.Extension; 4 | import jakarta.inject.Inject; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import org.eclipse.microprofile.config.Config; 7 | import org.eclipse.microprofile.config.spi.ConfigSource; 8 | import org.jboss.arquillian.container.test.api.Deployment; 9 | import org.jboss.arquillian.junit.Arquillian; 10 | import org.jboss.shrinkwrap.api.ShrinkWrap; 11 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author Phillip Kruger 17 | */ 18 | @RunWith(Arquillian.class) 19 | public class EnabledWhenEnabledKeyIsMissingTest { 20 | 21 | @Inject 22 | Config config; 23 | 24 | @Deployment 25 | public static JavaArchive createDeployment() { 26 | return ShrinkWrap.create(JavaArchive.class) 27 | .addPackages(true, Config.class.getPackage()) 28 | .addAsServiceProviderAndClasses(ConfigSource.class, XmlConfigSource.class) 29 | .addAsResource(EnabledWhenEnabledKeyIsMissingTest.class.getClassLoader().getResource("config-empty.properties"), "META-INF/microprofile-config.properties") 30 | .addAsManifestResource("META-INF/beans.xml"); 31 | } 32 | 33 | @Test 34 | public void testPropertyLoadsWhenNotExplicitlyEnabled() { 35 | assertThat(config.getOptionalValue("test.property", String.class)).get() 36 | .isEqualTo("a-string-value") 37 | .as("test.property in application.properties is set to a-string-value"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /configsource-xml/src/test/java/org/microprofileext/config/source/xml/EnabledWhenEnabledKeyIsTrueTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.xml; 2 | 3 | import jakarta.enterprise.inject.spi.Extension; 4 | import jakarta.inject.Inject; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import org.eclipse.microprofile.config.Config; 7 | import org.eclipse.microprofile.config.spi.ConfigSource; 8 | import org.jboss.arquillian.container.test.api.Deployment; 9 | import org.jboss.arquillian.junit.Arquillian; 10 | import org.jboss.shrinkwrap.api.ShrinkWrap; 11 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author Phillip Kruger 17 | */ 18 | @RunWith(Arquillian.class) 19 | public class EnabledWhenEnabledKeyIsTrueTest { 20 | 21 | @Inject 22 | Config config; 23 | 24 | @Deployment 25 | public static JavaArchive createDeployment() { 26 | return ShrinkWrap.create(JavaArchive.class) 27 | .addPackages(true, Config.class.getPackage()) 28 | .addAsServiceProviderAndClasses(ConfigSource.class, XmlConfigSource.class) 29 | .addAsResource(EnabledWhenEnabledKeyIsTrueTest.class.getClassLoader().getResource("config-enabled.properties"), "META-INF/microprofile-config.properties") 30 | .addAsManifestResource("META-INF/beans.xml"); 31 | } 32 | 33 | @Test 34 | public void testPropertyLoadsWhenExplicitlyEnabled() { 35 | assertThat(config.getOptionalValue("test.property", String.class)).get() 36 | .isEqualTo("a-string-value") 37 | .as("test.property in application.properties is set to a-string-value"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /configsource-xml/src/test/java/org/microprofileext/config/source/xml/ListTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.xml; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | import jakarta.enterprise.inject.spi.Extension; 6 | import jakarta.inject.Inject; 7 | import org.eclipse.microprofile.config.Config; 8 | import org.eclipse.microprofile.config.inject.ConfigProperty; 9 | import org.eclipse.microprofile.config.spi.ConfigSource; 10 | import org.jboss.arquillian.container.test.api.Deployment; 11 | import org.jboss.arquillian.junit.Arquillian; 12 | import org.jboss.shrinkwrap.api.ShrinkWrap; 13 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 14 | import org.junit.Assert; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | 18 | /** 19 | * @author Phillip Kruger 20 | */ 21 | @RunWith(Arquillian.class) 22 | public class ListTest { 23 | 24 | @Inject 25 | @ConfigProperty(name = "listTest") 26 | String stringList; 27 | 28 | @Inject 29 | @ConfigProperty(name = "listTest") 30 | List listList; 31 | 32 | @Inject 33 | @ConfigProperty(name = "listTest") 34 | Set setList; 35 | 36 | @Inject 37 | @ConfigProperty(name = "listTest") 38 | String[] arrayList; 39 | 40 | @Inject 41 | @ConfigProperty(name = "deepList.level1") 42 | List deepList; 43 | 44 | @Deployment 45 | public static JavaArchive createDeployment() { 46 | return ShrinkWrap.create(JavaArchive.class) 47 | .addPackages(true, Config.class.getPackage()) 48 | .addAsServiceProviderAndClasses(ConfigSource.class, XmlConfigSource.class) 49 | .addAsManifestResource("META-INF/beans.xml"); 50 | } 51 | 52 | @Test 53 | public void testStringList() { 54 | Assert.assertEquals("item1,item2,item3\\,stillItem3", stringList); 55 | } 56 | 57 | @Test 58 | public void testListList() { 59 | Assert.assertNotNull(listList); 60 | Assert.assertEquals(3, listList.size()); 61 | 62 | } 63 | 64 | @Test 65 | public void testSetList() { 66 | Assert.assertNotNull(setList); 67 | Assert.assertEquals(3, setList.size()); 68 | } 69 | 70 | @Test 71 | public void testArrayList() { 72 | Assert.assertNotNull(arrayList); 73 | Assert.assertEquals(3, arrayList.length); 74 | } 75 | 76 | @Test 77 | public void testDeepList(){ 78 | Assert.assertNotNull(deepList); 79 | Assert.assertEquals(2, deepList.size()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /configsource-xml/src/test/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /configsource-xml/src/test/resources/config-disabled.properties: -------------------------------------------------------------------------------- 1 | XmlConfigSource.enabled=false 2 | -------------------------------------------------------------------------------- /configsource-xml/src/test/resources/config-empty.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /configsource-xml/src/test/resources/config-enabled.properties: -------------------------------------------------------------------------------- 1 | XmlConfigSource.enabled=true 2 | -------------------------------------------------------------------------------- /configsource-yaml/README.md: -------------------------------------------------------------------------------- 1 | [Back to config-ext](https://github.com/microprofile-extensions/config-ext/blob/master/README.md) 2 | 3 | # Yaml Config Source 4 | 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-yaml/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.microprofile-ext.config-ext/configsource-yaml) 6 | [![Javadocs](https://www.javadoc.io/badge/org.microprofile-ext.config-ext/configsource-yaml.svg)](https://www.javadoc.io/doc/org.microprofile-ext.config-ext/configsource-yaml) 7 | 8 | This source gets values from some yaml file(s). 9 | 10 | ## Usage 11 | 12 | ```xml 13 | 14 | 15 | org.microprofile-ext.config-ext 16 | configsource-yaml 17 | XXXXX 18 | runtime 19 | 20 | 21 | ``` 22 | 23 | ## Example: 24 | 25 | ```yaml 26 | 27 | location: 28 | protocol: "http" 29 | host: "localhost" 30 | port: "8080" 31 | path: "/some/path" 32 | jedis: 33 | - Yoda 34 | - Qui-Gon Jinn 35 | - Obi-Wan Kenobi 36 | - Luke Skywalker 37 | ``` 38 | 39 | will create the following properties: 40 | 41 | ```property 42 | 43 | "location.protocol": "http" 44 | "location.host": "localhost" 45 | "location.port": "8080" 46 | "location.path": "/some/path" 47 | "location.jedis": "Yoda, Qui-Gon Jinn, Obi-Wan Kenobi, Luke Skywalker" 48 | 49 | ``` 50 | 51 | You can `inject` the jedis using any of the following: 52 | 53 | ```java 54 | 55 | @Inject 56 | @ConfigProperty(name = "location.jedis") 57 | String jedisAsString; 58 | 59 | @Inject 60 | @ConfigProperty(name = "location.jedis") 61 | List jedisAsList; 62 | 63 | @Inject 64 | @ConfigProperty(name = "location.jedis") 65 | Set jedisAsSet; 66 | 67 | @Inject 68 | @ConfigProperty(name = "location.jedis") 69 | String[] jedisAsArray; 70 | 71 | ``` 72 | 73 | ## Configure options 74 | 75 | ### Url(s) 76 | 77 | By default the config source will look for a file called `application.yaml`. You can set the location(s) of the files: 78 | 79 | configsource.yaml.url= 80 | 81 | example: 82 | 83 | configsource.yaml.url=file:/tmp/myconfig.yml 84 | 85 | You can also add more than one location by comma-separating the location: 86 | 87 | configsource.yaml.url=file:/tmp/myconfig.yml,http://localhost/myconfig.yml 88 | 89 | The latest files will override properties in previous files. As example, if using above configuration, property `foo=bar` in `file:/tmp/myconfig.yml` will be override if it's added to `http://localhost/myconfig.yml`. 90 | 91 | ### Detecting changes. 92 | 93 | You can watch the resource for changes. This feature is disabled by default. To enable: 94 | 95 | configsource.yaml.pollForChanges=true 96 | 97 | By default it will poll every **5 seconds**. You can change that, example to poll every 5 minutes: 98 | 99 | configsource.yaml.pollInterval=300 100 | 101 | ### Events 102 | 103 | This config source fires CDI Events on changes (if above detecting for changes is enabled). 104 | 105 | Read more about [Config Events](https://github.com/microprofile-extensions/config-ext/blob/master/config-events/README.md) 106 | 107 | You can disable this with the `configsource.yaml.notifyOnChanges` property: 108 | 109 | configsource.yaml.notifyOnChanges=false 110 | 111 | If you added more than one resource as source, the event will only fire if the resulting file change also changed the global source change, as one file takes priority over the other. 112 | 113 | ### Key separator 114 | 115 | By default the separator used in the key is a DOT (.) example: 116 | 117 | ```property 118 | 119 | "location.protocol": "http" 120 | ``` 121 | 122 | You can change this by setting `configsource.yaml.keyseparator` to the desired separator, example: 123 | 124 | configsource.yaml.keyseparator=_ 125 | 126 | will create: 127 | 128 | ```property 129 | 130 | "location_protocol": "http" 131 | ``` 132 | -------------------------------------------------------------------------------- /configsource-yaml/application.yaml: -------------------------------------------------------------------------------- 1 | test: 2 | property: "a-string-value" 3 | deepList: 4 | level1: 5 | - l1 6 | - l2 7 | listTest: 8 | - item1 9 | - item2 10 | - item3,stillItem3 11 | 12 | 13 | -------------------------------------------------------------------------------- /configsource-yaml/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.microprofile-ext 6 | config-ext 7 | 3.0.2-SNAPSHOT 8 | 9 | 10 | org.microprofile-ext.config-ext 11 | configsource-yaml 12 | 13 | Microprofile Config Extensions :: Yaml config source 14 | A config that get the values from a yaml file 15 | 16 | 17 | 18 | org.yaml 19 | snakeyaml 20 | 2.0 21 | 22 | 23 | 24 | ${project.groupId} 25 | configsource-filebase 26 | ${project.version} 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /configsource-yaml/src/main/java/org/microprofileext/config/source/yaml/YamlConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.yaml; 2 | 3 | import java.io.InputStream; 4 | import java.util.*; 5 | 6 | import org.microprofileext.config.source.base.file.AbstractUrlBasedSource; 7 | import org.yaml.snakeyaml.Yaml; 8 | 9 | /** 10 | * Yaml config source 11 | * @author Phillip Kruger 12 | */ 13 | public class YamlConfigSource extends AbstractUrlBasedSource { 14 | 15 | @Override 16 | protected String getFileExtension() { 17 | return "yaml"; 18 | } 19 | 20 | @Override 21 | @SuppressWarnings("unchecked") 22 | protected Map toMap(InputStream inputStream) { 23 | final Map properties = new TreeMap<>(); 24 | Yaml yaml = new Yaml(); 25 | Map yamlInput = yaml.loadAs(inputStream, TreeMap.class); 26 | 27 | if(Objects.nonNull(yamlInput)) { 28 | for (String key : yamlInput.keySet()) { 29 | populateMap(properties,key, yamlInput.get(key)); 30 | } 31 | } 32 | return properties; 33 | } 34 | 35 | @SuppressWarnings("unchecked") 36 | private void populateMap(Map properties, String key, Object o) { 37 | if (o instanceof Map) { 38 | Map map = (Map)o; 39 | for (Object mapKey : map.keySet()) { 40 | populateEntry(properties, key,mapKey.toString(),map); 41 | } 42 | } else if (o instanceof List) { 43 | List l = toStringList((List)o); 44 | properties.put(key,String.join(COMMA, l)); 45 | } else{ 46 | if(o!=null)properties.put(key,o.toString()); 47 | } 48 | } 49 | 50 | @SuppressWarnings("unchecked") 51 | private void populateEntry(Map properties, String key, String mapKey, Map map){ 52 | String format = "%s" + super.getKeySeparator() + "%s"; 53 | if (map.get(mapKey) instanceof Map) { 54 | populateMap(properties, String.format(format, key, mapKey), (Map) map.get(mapKey)); 55 | } else if (map.get(mapKey) instanceof List) { 56 | List l = toStringList((List)map.get(mapKey)); 57 | properties.put(String.format(format, key, mapKey),String.join(COMMA, l)); 58 | } else { 59 | properties.put(String.format(format, key, mapKey), map.get(mapKey).toString()); 60 | } 61 | } 62 | 63 | private List toStringList(List l){ 64 | List nl = new ArrayList<>(); 65 | for(Object o:l){ 66 | String s = String.valueOf(o); 67 | if(s.contains(COMMA))s = s.replaceAll(COMMA, "\\\\,"); // Escape comma 68 | nl.add(s); 69 | } 70 | return nl; 71 | } 72 | 73 | private static final String COMMA = ","; 74 | } -------------------------------------------------------------------------------- /configsource-yaml/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | org.microprofileext.config.source.yaml.YamlConfigSource -------------------------------------------------------------------------------- /configsource-yaml/src/test/java/org/microprofileext/config/source/yaml/DisabledWhenEnabledKeyIsFalseTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.yaml; 2 | 3 | import java.util.NoSuchElementException; 4 | import jakarta.enterprise.inject.spi.Extension; 5 | import jakarta.inject.Inject; 6 | import org.eclipse.microprofile.config.Config; 7 | import org.eclipse.microprofile.config.spi.ConfigSource; 8 | import org.jboss.arquillian.container.test.api.Deployment; 9 | import org.jboss.arquillian.junit.Arquillian; 10 | import org.jboss.shrinkwrap.api.ShrinkWrap; 11 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author Phillip Kruger 17 | */ 18 | @RunWith(Arquillian.class) 19 | public class DisabledWhenEnabledKeyIsFalseTest { 20 | 21 | @Inject 22 | Config config; 23 | 24 | @Deployment 25 | public static JavaArchive createDeployment() { 26 | return ShrinkWrap.create(JavaArchive.class) 27 | .addPackages(true, Config.class.getPackage()) 28 | .addAsServiceProviderAndClasses(ConfigSource.class, YamlConfigSource.class) 29 | .addAsResource(DisabledWhenEnabledKeyIsFalseTest.class.getClassLoader().getResource("config-disabled.properties"), "META-INF/microprofile-config.properties") 30 | .addAsManifestResource("META-INF/beans.xml"); 31 | } 32 | 33 | @Test(expected = NoSuchElementException.class) 34 | public void testPropertyFailsWhenExplicitlyDisabled() { 35 | config.getValue("test.property", String.class); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /configsource-yaml/src/test/java/org/microprofileext/config/source/yaml/EnabledWhenEnabledKeyIsMissingTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.yaml; 2 | 3 | import jakarta.enterprise.inject.spi.Extension; 4 | import jakarta.inject.Inject; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import org.eclipse.microprofile.config.Config; 7 | import org.eclipse.microprofile.config.spi.ConfigSource; 8 | import org.jboss.arquillian.container.test.api.Deployment; 9 | import org.jboss.arquillian.junit.Arquillian; 10 | import org.jboss.shrinkwrap.api.ShrinkWrap; 11 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author Phillip Kruger 17 | */ 18 | @RunWith(Arquillian.class) 19 | public class EnabledWhenEnabledKeyIsMissingTest { 20 | 21 | @Inject 22 | Config config; 23 | 24 | @Deployment 25 | public static JavaArchive createDeployment() { 26 | return ShrinkWrap.create(JavaArchive.class) 27 | .addPackages(true, Config.class.getPackage()) 28 | .addAsServiceProviderAndClasses(ConfigSource.class, YamlConfigSource.class) 29 | .addAsResource(EnabledWhenEnabledKeyIsMissingTest.class.getClassLoader().getResource("config-empty.properties"), "META-INF/microprofile-config.properties") 30 | .addAsManifestResource("META-INF/beans.xml"); 31 | } 32 | 33 | @Test 34 | public void testPropertyLoadsWhenNotExplicitlyEnabled() { 35 | assertThat(config.getOptionalValue("test.property", String.class)).get() 36 | .isEqualTo("a-string-value") 37 | .as("test.property in application.properties is set to a-string-value"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /configsource-yaml/src/test/java/org/microprofileext/config/source/yaml/EnabledWhenEnabledKeyIsTrueTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.yaml; 2 | 3 | import jakarta.enterprise.inject.spi.Extension; 4 | import jakarta.inject.Inject; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import org.eclipse.microprofile.config.Config; 7 | import org.eclipse.microprofile.config.spi.ConfigSource; 8 | import org.jboss.arquillian.container.test.api.Deployment; 9 | import org.jboss.arquillian.junit.Arquillian; 10 | import org.jboss.shrinkwrap.api.ShrinkWrap; 11 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author Phillip Kruger 17 | */ 18 | @RunWith(Arquillian.class) 19 | public class EnabledWhenEnabledKeyIsTrueTest { 20 | 21 | @Inject 22 | Config config; 23 | 24 | @Deployment 25 | public static JavaArchive createDeployment() { 26 | return ShrinkWrap.create(JavaArchive.class) 27 | .addPackages(true, Config.class.getPackage()) 28 | .addAsServiceProviderAndClasses(ConfigSource.class, YamlConfigSource.class) 29 | .addAsResource(EnabledWhenEnabledKeyIsTrueTest.class.getClassLoader().getResource("config-enabled.properties"), "META-INF/microprofile-config.properties") 30 | .addAsManifestResource("META-INF/beans.xml"); 31 | } 32 | 33 | @Test 34 | public void testPropertyLoadsWhenExplicitlyEnabled() { 35 | assertThat(config.getOptionalValue("test.property", String.class)).get() 36 | .isEqualTo("a-string-value") 37 | .as("test.property in application.properties is set to a-string-value"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /configsource-yaml/src/test/java/org/microprofileext/config/source/yaml/ListTest.java: -------------------------------------------------------------------------------- 1 | package org.microprofileext.config.source.yaml; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | import jakarta.enterprise.inject.spi.Extension; 6 | import jakarta.inject.Inject; 7 | import org.eclipse.microprofile.config.Config; 8 | import org.eclipse.microprofile.config.inject.ConfigProperty; 9 | import org.eclipse.microprofile.config.spi.ConfigSource; 10 | import org.jboss.arquillian.container.test.api.Deployment; 11 | import org.jboss.arquillian.junit.Arquillian; 12 | import org.jboss.shrinkwrap.api.ShrinkWrap; 13 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 14 | import org.junit.Assert; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | 18 | /** 19 | * @author Phillip Kruger 20 | */ 21 | @RunWith(Arquillian.class) 22 | public class ListTest { 23 | 24 | @Inject 25 | @ConfigProperty(name = "listTest") 26 | String stringList; 27 | 28 | @Inject 29 | @ConfigProperty(name = "listTest") 30 | List listList; 31 | 32 | @Inject 33 | @ConfigProperty(name = "listTest") 34 | Set setList; 35 | 36 | @Inject 37 | @ConfigProperty(name = "listTest") 38 | String[] arrayList; 39 | 40 | @Inject 41 | @ConfigProperty(name = "deepList.level1") 42 | List deepList; 43 | 44 | @Deployment 45 | public static JavaArchive createDeployment() { 46 | return ShrinkWrap.create(JavaArchive.class) 47 | .addPackages(true, Config.class.getPackage()) 48 | .addAsServiceProviderAndClasses(ConfigSource.class, YamlConfigSource.class) 49 | .addAsManifestResource("META-INF/beans.xml"); 50 | } 51 | 52 | @Test 53 | public void testStringList() { 54 | Assert.assertEquals("item1,item2,item3\\,stillItem3", stringList); 55 | } 56 | 57 | @Test 58 | public void testListList() { 59 | Assert.assertNotNull(listList); 60 | Assert.assertEquals(3, listList.size()); 61 | 62 | } 63 | 64 | @Test 65 | public void testSetList() { 66 | Assert.assertNotNull(setList); 67 | Assert.assertEquals(3, setList.size()); 68 | } 69 | 70 | @Test 71 | public void testArrayList() { 72 | Assert.assertNotNull(arrayList); 73 | Assert.assertEquals(3, arrayList.length); 74 | } 75 | 76 | @Test 77 | public void testDeepList(){ 78 | Assert.assertNotNull(deepList); 79 | Assert.assertEquals(2, deepList.size()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /configsource-yaml/src/test/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /configsource-yaml/src/test/resources/config-disabled.properties: -------------------------------------------------------------------------------- 1 | YamlConfigSource.enabled=false 2 | -------------------------------------------------------------------------------- /configsource-yaml/src/test/resources/config-empty.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /configsource-yaml/src/test/resources/config-enabled.properties: -------------------------------------------------------------------------------- 1 | YamlConfigSource.enabled=true 2 | -------------------------------------------------------------------------------- /nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CUSTOM-version up 5 | version up 6 | 7 | release:update-versions 8 | 9 | 10 | 11 | CUSTOM-release 12 | release 13 | 14 | versions:set 15 | -DremoveSnapshot 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------