diff --git a/pom.xml b/pom.xml
index 2b1ae1a10..d55384cc5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,6 +48,7 @@
docs
config-sources
converters/json
+ utils/events
diff --git a/utils/events/README.md b/utils/events/README.md
new file mode 100644
index 000000000..d47fe8f71
--- /dev/null
+++ b/utils/events/README.md
@@ -0,0 +1,163 @@
+# Config events
+
+Util library for config sources that fire events on changes.
+
+## Usage
+
+```xml
+
+
+ io.smallrye.config
+ smallrye-config-events
+ XXXX
+
+
+```
+
+## The event
+
+The CDI Event is a `ChangeEvent` and contains the following fields:
+
+* String key
+* Optional (String) oldValue
+* String newValue
+* Type type
+* String fromSource
+
+There are 3 types:
+
+* NEW - When you create a new key and value (i.e. the key does not exist anywhere in any config source)
+* UPDATE - When you update a value of an existing key (i.e. the key and value exist somewhere in a config source)
+* REMOVE - When you remove the value from the source (and that changed the overall config)
+
+### Observing events:
+
+You can listen to all or some of these events, filtering by `type` and/or `key` and/or `source`, example:
+
+```java
+
+ // Getting all config event
+ public void all(@Observes ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "ALL: Received a config change event: {0}", changeEvent);
+ }
+
+ // Get only new values
+ public void newValue(@Observes @TypeFilter(Type.NEW) ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "NEW: Received a config change event: {0}", changeEvent);
+ }
+
+ // Get only override values
+ public void overrideValue(@Observes @TypeFilter(Type.UPDATE) ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "UPDATE: Received a config change event: {0}", changeEvent);
+ }
+
+ // Get only revert values
+ public void revertValue(@Observes @TypeFilter(Type.REMOVE) ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "REMOVE: Received a config change event: {0}", changeEvent);
+ }
+
+ // Getting all config event when key is some.key
+ public void allForKey(@Observes @KeyFilter("some.key") ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "ALL for key [some.key]: Received a config change event: {0}", changeEvent);
+ }
+
+ // Getting all config event when key is some.key for new events
+ public void newForKey(@Observes @TypeFilter(Type.NEW) @KeyFilter("some.key") ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "NEW for key [some.key]: Received a config change event: {0}", changeEvent);
+ }
+
+ // Getting all config event when key is some.key for override events
+ public void overrideForKey(@Observes @TypeFilter(Type.UPDATE) @KeyFilter("some.key") ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "UPDATE for key [some.key]: Received a config change event: {0}", changeEvent);
+ }
+
+ // Getting all config event when key is some.key for revert events
+ public void revertForKey(@Observes @TypeFilter(Type.REMOVE) @KeyFilter("some.key") ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "REMOVE for key [some.key]: Received a config change event: {0}", changeEvent);
+ }
+
+ // Getting all config events for a certain source
+ public void allForSource(@Observes @SourceFilter("MemoryConfigSource") ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "ALL for source [MemoryConfigSource]: Received a config change event: {0}", changeEvent);
+ }
+
+ // Getting all config events for a certain source
+ public void allForSourceAndKey(@Observes @SourceFilter("MemoryConfigSource") @KeyFilter("some.key") ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "ALL for source [MemoryConfigSource] and for key [some.key]: Received a config change event: {0}", changeEvent);
+ }
+
+ // Getting all config events for a certain source
+ public void overrideForSourceAndKey(@Observes @TypeFilter(Type.UPDATE) @SourceFilter("MemoryConfigSource") @KeyFilter("some.key") ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "UPDATE for source [MemoryConfigSource] and for key [some.key]: Received a config change event: {0}", changeEvent);
+ }
+
+```
+
+Note: You can filter by including the `@TypeFilter` and/or the `@KeyFilter` and/or the `@SourceFilter`.
+
+
+### Pattern matching on field.
+
+You might want to listen for fields that match a certain regex.
+
+Example, listen to all keys that starts with `some.`:
+
+```java
+
+ @RegexFilter("^some\\..+")
+ public void allForPatternMatchOnKey(@Observes ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "Pattern match on key: Received a config change event: {0}", changeEvent);
+ }
+
+```
+
+By default, it will match on `key`, however you also listen on another field,
+for example, listen to all `oldValue` that starts with `some.`:
+
+```java
+
+ @RegexFilter(onField = Field.oldValue, value = "^some\\..+")
+ public void allForPatternMatchOnOldValue(@Observes ChangeEvent changeEvent){
+ log.log(Level.SEVERE, "Pattern match on old value: Received a config change event: {0}", changeEvent);
+ }
+
+```
+
+You can Match on the following fields of the `ChangeEvent` object:
+
+* key
+* oldValue
+* newValue
+* fromSource
+
+## Implementing this for your own Config source
+
+An example of a source that uses this is [Memory Config source](https://github.com/smallrye/smallrye-config/tree/master/extensions/sources/memory)
+
+`io.smallrye.config.events.ChangeEventNotifier` is a bean that makes it easy to detect changes and fire the appropriate events.
+
+To use it in your own source:
+
+* Get a snapshot of the properties before the change.
+* Get a snapshot of the properties after the change.
+* Call `detectChangesAndFire` method:
+
+Example:
+
+```java
+
+ Map before = new HashMap<>(memoryConfigSource.getProperties());
+ memoryConfigSource.getProperties().remove(key);
+ Map after = new HashMap<>(memoryConfigSource.getProperties());
+ ChangeEventNotifier.getInstance().detectChangesAndFire(before, after,MemoryConfigSource.NAME)
+
+```
+
+or if you know the change and do not need detection:
+
+```java
+
+ memoryConfigSource.getProperties().remove(key);
+ ChangeEventNotifier.getInstance().fire(new ChangeEvent(Type.REMOVE,key,getOptionalOldValue(oldValue),null,MemoryConfigSource.NAME));
+
+```
\ No newline at end of file
diff --git a/utils/events/pom.xml b/utils/events/pom.xml
new file mode 100644
index 000000000..375a7f76f
--- /dev/null
+++ b/utils/events/pom.xml
@@ -0,0 +1,63 @@
+
+
+ 4.0.0
+
+
+ io.smallrye
+ smallrye-config-parent
+ 1.3.9-SNAPSHOT
+ ../../
+
+
+ io.smallrye.config
+ smallrye-config-events
+
+ jar
+ SmallRye: MicroProfile Config Events
+ A library to make it easy to add config events to config sources
+
+
+
+ javax.enterprise
+ cdi-api
+ provided
+
+
+ javax.annotation
+ javax.annotation-api
+ provided
+
+
+
+
+ junit
+ junit
+ test
+
+
+ io.smallrye
+ smallrye-config
+ test
+
+
+ org.jboss.arquillian.container
+ arquillian-weld-embedded
+ test
+
+
+ org.jboss.arquillian.junit
+ arquillian-junit-container
+ test
+
+
+ org.jboss.shrinkwrap.resolver
+ shrinkwrap-resolver-impl-maven
+ test
+
+
+ org.jboss.weld
+ weld-core-impl
+ test
+
+
+
\ No newline at end of file
diff --git a/utils/events/src/main/java/io/smallrye/config/events/ChangeEvent.java b/utils/events/src/main/java/io/smallrye/config/events/ChangeEvent.java
new file mode 100644
index 000000000..895b70f9d
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/ChangeEvent.java
@@ -0,0 +1,52 @@
+package io.smallrye.config.events;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+/**
+ * an Event on a config element
+ *
+ * @author Phillip Kruger
+ */
+public class ChangeEvent implements Serializable {
+
+ private final Type type;
+ private final String key;
+ private final Optional oldValue;
+ private final String newValue;
+ private final String fromSource;
+
+ public ChangeEvent(Type type, String key, Optional oldValue, String newValue, String fromSource) {
+ this.type = type;
+ this.key = key;
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ this.fromSource = fromSource;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Optional getOldValue() {
+ return oldValue;
+ }
+
+ public String getNewValue() {
+ return newValue;
+ }
+
+ public String getFromSource() {
+ return fromSource;
+ }
+
+ @Override
+ public String toString() {
+ return "ChangeEvent{" + "type=" + type + ", key=" + key + ", oldValue=" + oldValue + ", newValue=" + newValue
+ + ", fromSource=" + fromSource + '}';
+ }
+}
diff --git a/utils/events/src/main/java/io/smallrye/config/events/ChangeEventNotifier.java b/utils/events/src/main/java/io/smallrye/config/events/ChangeEventNotifier.java
new file mode 100644
index 000000000..e5fd2d647
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/ChangeEventNotifier.java
@@ -0,0 +1,96 @@
+package io.smallrye.config.events;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.event.Event;
+import javax.enterprise.event.Observes;
+import javax.inject.Inject;
+
+/**
+ * Easy way to fire a change event
+ *
+ * @author Phillip Kruger
+ *
+ * This gets used from Config sources that is not in the CDI Context. So we can not @Inject a bean.
+ * For some reason, CDI.current() is only working on Payara, and not on Thorntail and OpenLiberty, so this ugly footwork
+ * is to
+ * get around that.
+ */
+@ApplicationScoped
+public class ChangeEventNotifier {
+
+ @Inject
+ private Event broadcaster;
+
+ private static ChangeEventNotifier INSTANCE;
+
+ public void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
+ INSTANCE = this;
+ }
+
+ public static ChangeEventNotifier getInstance() {
+ // return CDI.current().select(ChangeEventNotifier.class).get();
+ return INSTANCE;
+ }
+
+ public void detectChangesAndFire(Map before, Map after, String fromSource) {
+ List changes = new ArrayList<>();
+ if (!before.equals(after)) {
+ Set> beforeEntries = before.entrySet();
+ for (Map.Entry beforeEntry : beforeEntries) {
+ String key = beforeEntry.getKey();
+ String oldValue = beforeEntry.getValue();
+ if (after.containsKey(key)) {
+ String newValue = after.get(key);
+ // Value can be null !
+ if ((oldValue != null && newValue == null) ||
+ (newValue != null && oldValue == null) ||
+ (newValue != null && oldValue != null && !newValue.equals(oldValue))) {
+ // Update
+ changes.add(new ChangeEvent(Type.UPDATE, key, getOptionalOldValue(oldValue), newValue, fromSource));
+ }
+ after.remove(key);
+ } else {
+ // Removed.
+ changes.add(new ChangeEvent(Type.REMOVE, key, getOptionalOldValue(oldValue), null, fromSource));
+ }
+ }
+ Set> newEntries = after.entrySet();
+ for (Map.Entry newEntry : newEntries) {
+ // New
+ changes.add(new ChangeEvent(Type.NEW, newEntry.getKey(), Optional.empty(), newEntry.getValue(), fromSource));
+ }
+ }
+ if (!changes.isEmpty())
+ fire(changes);
+ }
+
+ public void fire(ChangeEvent changeEvent) {
+ List annotationList = new ArrayList<>();
+ annotationList.add(new TypeFilter.TypeFilterLiteral(changeEvent.getType()));
+ annotationList.add(new KeyFilter.KeyFilterLiteral(changeEvent.getKey()));
+ annotationList.add(new SourceFilter.SourceFilterLiteral(changeEvent.getFromSource()));
+
+ broadcaster.select(annotationList.toArray(new Annotation[annotationList.size()])).fire(changeEvent);
+ }
+
+ public void fire(List changeEvents) {
+ for (ChangeEvent changeEvent : changeEvents) {
+ fire(changeEvent);
+ }
+ }
+
+ public Optional getOptionalOldValue(String oldValue) {
+ if (oldValue == null || oldValue.isEmpty())
+ return Optional.empty();
+ return Optional.of(oldValue);
+ }
+
+}
diff --git a/utils/events/src/main/java/io/smallrye/config/events/KeyFilter.java b/utils/events/src/main/java/io/smallrye/config/events/KeyFilter.java
new file mode 100644
index 000000000..94f9c4153
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/KeyFilter.java
@@ -0,0 +1,36 @@
+package io.smallrye.config.events;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Qualifier;
+
+/**
+ * Filter the event on the key
+ *
+ * @author Phillip Kruger
+ */
+@Qualifier
+@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface KeyFilter {
+ String value();
+
+ class KeyFilterLiteral extends AnnotationLiteral implements KeyFilter {
+ private final String key;
+
+ KeyFilterLiteral(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public String value() {
+ return this.key;
+ }
+ }
+}
diff --git a/utils/events/src/main/java/io/smallrye/config/events/SourceFilter.java b/utils/events/src/main/java/io/smallrye/config/events/SourceFilter.java
new file mode 100644
index 000000000..dcbf7bd36
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/SourceFilter.java
@@ -0,0 +1,36 @@
+package io.smallrye.config.events;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Qualifier;
+
+/**
+ * Filter by a config source
+ *
+ * @author Phillip Kruger
+ */
+@Qualifier
+@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface SourceFilter {
+ String value();
+
+ class SourceFilterLiteral extends AnnotationLiteral implements SourceFilter {
+ private final String name;
+
+ SourceFilterLiteral(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String value() {
+ return this.name;
+ }
+ }
+}
diff --git a/utils/events/src/main/java/io/smallrye/config/events/Type.java b/utils/events/src/main/java/io/smallrye/config/events/Type.java
new file mode 100644
index 000000000..6e886ab16
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/Type.java
@@ -0,0 +1,7 @@
+package io.smallrye.config.events;
+
+public enum Type {
+ NEW,
+ REMOVE,
+ UPDATE
+}
\ No newline at end of file
diff --git a/utils/events/src/main/java/io/smallrye/config/events/TypeFilter.java b/utils/events/src/main/java/io/smallrye/config/events/TypeFilter.java
new file mode 100644
index 000000000..1efb4e85c
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/TypeFilter.java
@@ -0,0 +1,36 @@
+package io.smallrye.config.events;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Qualifier;
+
+/**
+ * filter by change type
+ *
+ * @author Phillip Kruger
+ */
+@Qualifier
+@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface TypeFilter {
+ Type value();
+
+ class TypeFilterLiteral extends AnnotationLiteral implements TypeFilter {
+ private final Type type;
+
+ TypeFilterLiteral(Type type) {
+ this.type = type;
+ }
+
+ @Override
+ public Type value() {
+ return this.type;
+ }
+ }
+}
diff --git a/utils/events/src/main/java/io/smallrye/config/events/regex/Field.java b/utils/events/src/main/java/io/smallrye/config/events/regex/Field.java
new file mode 100644
index 000000000..3de4fd11f
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/regex/Field.java
@@ -0,0 +1,13 @@
+package io.smallrye.config.events.regex;
+
+/**
+ * a field to apply a regex on
+ *
+ * @author Phillip Kruger
+ */
+public enum Field {
+ key,
+ oldValue,
+ newValue,
+ fromSource
+}
diff --git a/utils/events/src/main/java/io/smallrye/config/events/regex/RegexFilter.java b/utils/events/src/main/java/io/smallrye/config/events/regex/RegexFilter.java
new file mode 100644
index 000000000..e1b6c8064
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/regex/RegexFilter.java
@@ -0,0 +1,27 @@
+package io.smallrye.config.events.regex;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.Nonbinding;
+import javax.interceptor.InterceptorBinding;
+
+/**
+ * an interceptor that match the value to a regular expression
+ *
+ * @author Phillip Kruger
+ */
+@Inherited
+@InterceptorBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface RegexFilter {
+ @Nonbinding
+ String value();
+
+ @Nonbinding
+ Field onField() default Field.key;
+}
diff --git a/utils/events/src/main/java/io/smallrye/config/events/regex/RegexFilterInterceptor.java b/utils/events/src/main/java/io/smallrye/config/events/regex/RegexFilterInterceptor.java
new file mode 100644
index 000000000..f6a7ddb34
--- /dev/null
+++ b/utils/events/src/main/java/io/smallrye/config/events/regex/RegexFilterInterceptor.java
@@ -0,0 +1,76 @@
+package io.smallrye.config.events.regex;
+
+import java.util.Optional;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Priority;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+import io.smallrye.config.events.ChangeEvent;
+
+@RegexFilter(value = "")
+@Interceptor
+@Priority(100)
+public class RegexFilterInterceptor {
+ private static final Logger log = Logger.getLogger(RegexFilterInterceptor.class.getName());
+
+ @AroundInvoke
+ public Object observer(InvocationContext ctx) throws Exception {
+
+ RegexFilter regexFilterAnnotation = ctx.getMethod().getAnnotation(RegexFilter.class);
+ Field onField = regexFilterAnnotation.onField();
+ String regex = regexFilterAnnotation.value();
+
+ Optional posibleChangeEvent = getChangeEvent(ctx);
+
+ if (posibleChangeEvent.isPresent()) {
+ ChangeEvent changeEvent = posibleChangeEvent.get();
+ String value = getValueToApplyRegexOn(changeEvent, onField);
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(value);
+ boolean b = matcher.matches();
+ if (!b)
+ return null;
+ } else {
+ log.log(Level.WARNING, "Can not find ChangeEvent parameter for method {0}. @RegexFilter is being ignored",
+ ctx.getMethod().getName());
+ }
+ return ctx.proceed();
+ }
+
+ private String getValueToApplyRegexOn(ChangeEvent changeEvent, Field onField) {
+ String value = null;
+ switch (onField) {
+ case key:
+ value = changeEvent.getKey();
+ break;
+ case fromSource:
+ value = changeEvent.getFromSource();
+ break;
+ case newValue:
+ value = changeEvent.getNewValue();
+ break;
+ case oldValue:
+ value = changeEvent.getOldValue().orElse("");
+ }
+
+ return value;
+ }
+
+ private Optional getChangeEvent(InvocationContext ctx) {
+ Object[] parameters = ctx.getParameters();
+
+ for (Object parameter : parameters) {
+ if (parameter.getClass().equals(ChangeEvent.class)) {
+ ChangeEvent changeEvent = (ChangeEvent) parameter;
+ return Optional.of(changeEvent);
+ }
+ }
+ return Optional.empty();
+ }
+}
diff --git a/utils/events/src/main/resources/META-INF/beans.xml b/utils/events/src/main/resources/META-INF/beans.xml
new file mode 100644
index 000000000..ad2a3b6ec
--- /dev/null
+++ b/utils/events/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/utils/events/src/test/java/io/smallrye/config/events/ChangeEventNotifierTest.java b/utils/events/src/test/java/io/smallrye/config/events/ChangeEventNotifierTest.java
new file mode 100644
index 000000000..6b7fb6a13
--- /dev/null
+++ b/utils/events/src/test/java/io/smallrye/config/events/ChangeEventNotifierTest.java
@@ -0,0 +1,116 @@
+package io.smallrye.config.events;
+
+import java.io.File;
+import java.util.Optional;
+
+import javax.enterprise.event.Observes;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import io.smallrye.config.events.regex.RegexFilter;
+import io.smallrye.config.events.regex.RegexFilterInterceptor;
+
+/**
+ * Testing that the events fire correctly
+ *
+ * @author Phillip Kruger
+ */
+@RunWith(Arquillian.class)
+public class ChangeEventNotifierTest {
+
+ @Deployment
+ public static WebArchive createDeployment() {
+ final File[] smallryeConfig = Maven.resolver()
+ .loadPomFromFile("pom.xml")
+ .resolve("io.smallrye:smallrye-config")
+ .withoutTransitivity().asFile();
+
+ return ShrinkWrap.create(WebArchive.class, "ChangeEventNotifierTest.war")
+ .addPackage(ChangeEventNotifier.class.getPackage())
+ .addPackage(RegexFilterInterceptor.class.getPackage())
+ .addAsLibraries(smallryeConfig)
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+ }
+
+ @Test
+ public void testNewType() {
+ ChangeEvent changeEvent = new ChangeEvent(Type.NEW, "test.key", Optional.empty(), "test value", "TestCase");
+ ChangeEventNotifier.getInstance().fire(changeEvent);
+ }
+
+ @Test
+ public void testUpdateType() {
+ ChangeEvent changeEvent = new ChangeEvent(Type.UPDATE, "test.key", Optional.of("old value"), "test value", "TestCase");
+ ChangeEventNotifier.getInstance().fire(changeEvent);
+ }
+
+ @Test
+ public void testRemoveType() {
+ ChangeEvent changeEvent = new ChangeEvent(Type.REMOVE, "test.key", Optional.of("old value"), null, "TestCase");
+ ChangeEventNotifier.getInstance().fire(changeEvent);
+ }
+
+ @Test
+ public void testCertainKey() {
+ ChangeEvent changeEvent = new ChangeEvent(Type.UPDATE, "some.key", Optional.of("old value"), "test value", "TestCase");
+ ChangeEventNotifier.getInstance().fire(changeEvent);
+ }
+
+ @Test
+ public void testCertainKeyAndUpdate() {
+ ChangeEvent changeEvent = new ChangeEvent(Type.UPDATE, "some.key", Optional.of("old value"), "test value", "TestCase");
+ ChangeEventNotifier.getInstance().fire(changeEvent);
+ }
+
+ @Test
+ public void testCertainSource() {
+ ChangeEvent changeEvent = new ChangeEvent(Type.UPDATE, "some.key", Optional.of("old value"), "test value",
+ "SomeConfigSource");
+ ChangeEventNotifier.getInstance().fire(changeEvent);
+ }
+
+ @Test
+ public void testRegex() {
+ ChangeEvent changeEvent = new ChangeEvent(Type.NEW, "testcase.key", Optional.empty(), "test value", "TestCase");
+ ChangeEventNotifier.getInstance().fire(changeEvent);
+ }
+
+ public void listenForNew(@Observes @TypeFilter(Type.NEW) ChangeEvent changeEvent) {
+ Assert.assertEquals("Expecting new type", Type.NEW, changeEvent.getType());
+ }
+
+ public void listenForUpdate(@Observes @TypeFilter(Type.UPDATE) ChangeEvent changeEvent) {
+ Assert.assertEquals("Expecting update type", Type.UPDATE, changeEvent.getType());
+ }
+
+ public void listenForRemove(@Observes @TypeFilter(Type.REMOVE) ChangeEvent changeEvent) {
+ Assert.assertEquals("Expecting remove type", Type.REMOVE, changeEvent.getType());
+ }
+
+ public void listenForCertainKey(@Observes @KeyFilter("some.key") ChangeEvent changeEvent) {
+ Assert.assertEquals("Expecting certain key", "some.key", changeEvent.getKey());
+ }
+
+ public void listenForCertainKeyAndUpdate(
+ @Observes @TypeFilter(Type.UPDATE) @KeyFilter("some.key") ChangeEvent changeEvent) {
+ Assert.assertEquals("Expecting certain key", "some.key", changeEvent.getKey());
+ Assert.assertEquals("Expecting update type", Type.UPDATE, changeEvent.getType());
+ }
+
+ public void listenForCertainSource(@Observes @SourceFilter("SomeConfigSource") ChangeEvent changeEvent) {
+ Assert.assertEquals("Expecting certain config source", "SomeConfigSource", changeEvent.getFromSource());
+ }
+
+ @RegexFilter("^testcase\\..+")
+ public void listenForKeyPattern(@Observes ChangeEvent changeEvent) {
+ Assert.assertTrue("Expecting key to start with certain value", changeEvent.getKey().startsWith("testcase"));
+ }
+}