You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by jo...@apache.org on 2014/12/28 16:12:37 UTC

[4/4] incubator-tamaya git commit: Added a programmatic callback for changesets.

Added a programmatic callback for changesets.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/a1777439
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/a1777439
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/a1777439

Branch: refs/heads/0.1-prototype
Commit: a1777439892646023e62f8ce08c4bcaf109b4aca
Parents: dbca179
Author: John D. Ament <jo...@apache.org>
Authored: Sun Dec 28 09:39:46 2014 -0500
Committer: John D. Ament <jo...@apache.org>
Committed: Sun Dec 28 09:39:46 2014 -0500

----------------------------------------------------------------------
 .../java/org/apache/tamaya/ConfigChangeSet.java | 167 +++++++++
 .../apache/tamaya/ConfigChangeSetBuilder.java   | 356 ++++++++++++++++++
 .../java/org/apache/tamaya/Configuration.java   |  18 +-
 .../java/org/apache/tamaya/PropertySource.java  |  38 ++
 .../tamaya/spi/ConfigChangeSetCallback.java     |  33 ++
 .../tamaya/spi/ConfigurationFactorySpi.java     |  16 +
 .../tamaya/core/config/ConfigChangeSet.java     | 169 ---------
 .../core/config/ConfigChangeSetBuilder.java     | 359 -------------------
 .../core/properties/AbstractPropertySource.java |  20 ++
 9 files changed, 647 insertions(+), 529 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java b/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java
new file mode 100644
index 0000000..dda7701
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya;
+
+import java.beans.PropertyChangeEvent;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Event that contains a set current changes that were applied or could be applied.
+ * This class is immutable and thread-safe. To create instances use
+ * {@link ConfigChangeSetBuilder}.
+ *
+ * Created by Anatole on 22.10.2014.
+ */
+public final class ConfigChangeSet implements Serializable{
+
+    private static final long serialVersionUID = 1l;
+    /** The base property provider/configuration. */
+    private PropertySource propertySource;
+    /** The base version, usable for optimistic locking. */
+    private String baseVersion;
+    /** The recorded changes. */
+    private Map<String,PropertyChangeEvent> changes = new HashMap<>();
+
+    /**
+     * Get an empty change set for the given provider.
+     * @param propertyProvider The base property provider/configuration, not null.
+     * @return an empty ConfigChangeSet instance.
+     */
+    public static ConfigChangeSet emptyChangeSet(PropertySource propertyProvider){
+        return new ConfigChangeSet(propertyProvider, Collections.emptySet());
+    }
+
+    /**
+     * Constructor used by {@link ConfigChangeSetBuilder}.
+     * @param propertySource The base property provider/configuration, not null.
+     * @param changes The recorded changes, not null.
+     */
+    ConfigChangeSet(PropertySource propertySource, Collection<PropertyChangeEvent> changes) {
+        this.propertySource = Objects.requireNonNull(propertySource);
+        changes.forEach((c) -> this.changes.put(c.getPropertyName(), c));
+    }
+
+    /**
+     * Get the underlying property provider/configuration.
+     * @return the underlying property provider/configuration, never null.
+     */
+    public PropertySource getPropertySource(){
+        return this.propertySource;
+    }
+
+    /**
+     * Get the base version, usable for optimistic locking.
+     * @return the base version.
+     */
+    public String getBaseVersion(){
+        return baseVersion;
+    }
+
+    /**
+     * Get the changes recorded.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertyChangeEvent> getEvents(){
+        return Collections.unmodifiableCollection(this.changes.values());
+    }
+
+    /**
+     * Access the number current removed entries.
+     * @return the number current removed entries.
+     */
+    public int getRemovedSize() {
+        return (int) this.changes.values().stream().filter((e) -> e.getNewValue() == null).count();
+    }
+
+    /**
+     * Access the number current added entries.
+     * @return the number current added entries.
+     */
+    public int getAddedSize() {
+        return (int) this.changes.values().stream().filter((e) -> e.getOldValue() == null).count();
+    }
+
+    /**
+     * Access the number current updated entries.
+     * @return the number current updated entries.
+     */
+    public int getUpdatedSize() {
+        return (int) this.changes.values().stream().filter((e) -> e.getOldValue()!=null && e.getNewValue()!=null).count();
+    }
+
+
+    /**
+     * Checks if the given key was removed.
+     * @param key the target key, not null.
+     * @return true, if the given key was removed.
+     */
+    public boolean isRemoved(String key) {
+        PropertyChangeEvent change = this.changes.get(key);
+        return change != null && change.getNewValue() == null;
+    }
+
+    /**
+     * Checks if the given key was added.
+     * @param key the target key, not null.
+     * @return true, if the given key was added.
+     */
+    public boolean isAdded(String key) {
+        PropertyChangeEvent change = this.changes.get(key);
+        return change != null && change.getOldValue() == null;
+    }
+
+    /**
+     * Checks if the given key was updated.
+     * @param key the target key, not null.
+     * @return true, if the given key was updated.
+     */
+    public boolean isUpdated(String key) {
+        PropertyChangeEvent change = this.changes.get(key);
+        return change != null && change.getOldValue() != null && change.getNewValue() != null;
+    }
+
+    /**
+     * Checks if the given key is added, or updated AND NOT removed.
+     * @param key the target key, not null.
+     * @return true, if the given key was added, or updated BUT NOT removed.
+     */
+    public boolean containsKey(String key) {
+        PropertyChangeEvent change = this.changes.get(key);
+        return change != null && change.getNewValue() != null;
+    }
+
+    /**
+     * CHecks if the current change set does not contain any changes.
+     * @return tru, if the change set is empty.
+     */
+    public boolean isEmpty(){
+        return this.changes.isEmpty();
+    }
+
+
+    @Override
+    public String toString() {
+        return "ConfigChangeSet{" +
+                "properties=" + propertySource +
+                ", baseVersion=" + baseVersion +
+                ", changes=" + changes +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
new file mode 100644
index 0000000..a5ef042
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya;
+
+import java.beans.PropertyChangeEvent;
+import java.util.*;
+import java.util.function.Function;
+
+/**
+ * Models a set current changes to be applied to a configuration/property provider.  Such a set can be applied
+ * to any {@link org.apache.tamaya.PropertySource} instance. If the provider is mutable it may check the
+ * version given and applyChanges the changes to the provider/configuration, including triggering current regarding
+ * change events.
+ * <p>
+ * For appropriate conversion a {@code Function<String, Codec>} can be applied, which performs correct conversion,
+ * when changed values are set. This function enables connecting e.g. setters on a configuration template with
+ * the corresponding conversion logic, so the template calls are correctly converted back.
+ */
+public final class ConfigChangeSetBuilder {
+    /**
+     * The recorded changes.
+     */
+    final SortedMap<String, PropertyChangeEvent> delta = new TreeMap<>();
+    /**
+     * The underlying configuration/provider.
+     */
+    PropertySource source;
+
+    /**
+     * Constructor.
+     *
+     * @param source      the underlying configuration/provider, not null.
+     */
+    private ConfigChangeSetBuilder(PropertySource source) {
+        this.source = Objects.requireNonNull(source);
+    }
+
+    /**
+     * Creates a new instance current this builder.
+     *
+     * @param source the underlying property provider/configuration, not null.
+     * @return the builder for chaining.
+     */
+    public static ConfigChangeSetBuilder of(PropertySource source) {
+        return new ConfigChangeSetBuilder(source);
+    }
+
+
+    /**
+     * Creates a new instance current this builder.
+     *
+     * @param configuration the base configuration, not null.
+     * @return the builder for chaining.
+     */
+    public static ConfigChangeSetBuilder of(Configuration configuration) {
+        return new ConfigChangeSetBuilder(configuration);
+    }
+
+    /**
+     * This method records all changes to be applied to the base property provider/configuration to
+     * achieve the given target state.
+     *
+     * @param newState the new target state, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder addChanges(PropertySource newState) {
+        compare(newState, this.source).forEach((c) -> this.delta.put(c.getPropertyName(), c));
+        return this;
+    }
+
+    /**
+     * Get the current values, also considering any changes recorded within this change set.
+     *
+     * @param key the key current the entry, not null.
+     * @return the keys, or null.
+     */
+    public String get(String key) {
+        PropertyChangeEvent change = this.delta.get(key);
+        if (change != null && !(change.getNewValue() == null)) {
+            return (String) change.getNewValue();
+        }
+        return null;
+    }
+
+    /**
+     * Marks the given key(s) fromMap the configuration/properties to be removed.
+     *
+     * @param key       the key current the entry, not null.
+     * @param otherKeys additional keys to be removed (convenience), not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder remove(String key, String... otherKeys) {
+        String oldValue = this.source.get(key).orElse(null);
+        if (oldValue == null) {
+            this.delta.remove(key);
+        }
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, oldValue, null));
+        for (String addKey : otherKeys) {
+            oldValue = this.source.get(addKey).orElse(null);
+            if (oldValue == null) {
+                this.delta.remove(addKey);
+            }
+            this.delta.put(addKey, new PropertyChangeEvent(this.source, addKey, oldValue, null));
+        }
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, boolean value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     s* Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, byte value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, char value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, short value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, int value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, long value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, float value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, double value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder put(String key, String value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @return the builder for chaining.
+     * @throws org.apache.tamaya.ConfigException if no matching Codec could be found.
+     */
+    public <T> ConfigChangeSetBuilder put(String key, Class<T> type, T value) {
+        put(key, type, value, null);
+        return this;
+    }
+
+    /**
+     * Applies the given keys.
+     *
+     * @param key   the key current the entry, not null.
+     * @param value the keys to be applied, not null.
+     * @param adapter the codec to be used, if set overrides any other codecs that may apply. If null an appropriate
+     *              codec is tried to be evaluated as needed.
+     * @return the builder for chaining.
+     * @throws org.apache.tamaya.ConfigException if no matching Codec could be found.
+     */
+    public <T> ConfigChangeSetBuilder put(String key, Class<T> type, T value, Function<T,String> adapter) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), adapter.apply(Objects.requireNonNull(value))));
+        return this;
+    }
+
+
+    /**
+     * Apply all the given values to the base configuration/properties.
+     * Note that all values passed must be convertible to String, either
+     * <ul>
+     * <li>the registered codecs provider provides codecs for the corresponding keys, or </li>
+     * <li>default codecs are present for the given type, or</li>
+     * <li>the value is an instanceof String</li>
+     * </ul>
+     *
+     * @param changes the changes to be applied, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder putAll(Map<String, String> changes) {
+        changes.putAll(changes);
+        return this;
+    }
+
+    /**
+     * This method will create a change set that clears all entries fromMap the given base configuration/properties.
+     *
+     * @return the builder for chaining.
+     */
+    public ConfigChangeSetBuilder deleteAll() {
+        this.delta.clear();
+        this.source.getProperties().forEach((k, v) ->
+                this.delta.put(k, new PropertyChangeEvent(this.source, k, v, null)));
+        return this;
+    }
+
+    /**
+     * Checks if the change set is empty, i.e. does not contain any changes.
+     *
+     * @return true, if the set is empty.
+     */
+    public boolean isEmpty() {
+        return this.delta.isEmpty();
+    }
+
+    /**
+     * Resets this change set instance. This will clear all changes done to this builder, so the
+     * set will be empty.
+     */
+    public void reset() {
+        this.delta.clear();
+    }
+
+    /**
+     * Builds the corresponding change set.
+     *
+     * @return the new change set, never null.
+     */
+    public ConfigChangeSet build() {
+        return new ConfigChangeSet(this.source, Collections.unmodifiableCollection(this.delta.values()));
+    }
+
+    /**
+     * Compares the two property config/configurations and creates a collection current all changes
+     * that must be appied to render {@code map1} into {@code map2}.
+     *
+     * @param map1 the source map, not null.
+     * @param map2 the target map, not null.
+     * @return a collection current change events, never null.
+     */
+    public static Collection<PropertyChangeEvent> compare(PropertySource map1, PropertySource map2) {
+        List<PropertyChangeEvent> changes = new ArrayList<>();
+        for (Map.Entry<String, String> en : map1.getProperties().entrySet()) {
+            Optional<String> val = map2.get(en.getKey());
+            if (!val.isPresent()) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue()));
+            } else if (!val.get().equals(en.getValue())) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue()));
+            }
+        }
+        for (Map.Entry<String, String> en : map2.getProperties().entrySet()) {
+            Optional<String> val = map1.get(en.getKey());
+            if (!val.isPresent()) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue()));
+            } else if (!val.equals(Optional.ofNullable(en.getValue()))) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue()));
+            }
+        }
+        return changes;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "PropertyChangeEventBuilder [source=" + source + ", " +
+                ", delta=" + delta + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/api/src/main/java/org/apache/tamaya/Configuration.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/Configuration.java b/api/src/main/java/org/apache/tamaya/Configuration.java
index 73eb41a..0999b1f 100644
--- a/api/src/main/java/org/apache/tamaya/Configuration.java
+++ b/api/src/main/java/org/apache/tamaya/Configuration.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tamaya;
 
+import org.apache.tamaya.spi.ConfigChangeSetCallback;
 import org.apache.tamaya.spi.ConfigurationFactorySpi;
 import org.apache.tamaya.spi.ConfigurationSpi;
 import org.apache.tamaya.spi.ServiceContext;
@@ -61,13 +62,28 @@ public interface Configuration extends PropertyMapSupplier,PropertySource {
         }
 
         @Override
+        public void update(ConfigChangeSet changeSet) {
+
+        }
+
+        @Override
+        public void registerForUpdate(ConfigChangeSetCallback callback) {
+
+        }
+
+        @Override
+        public void removeForUpdate(ConfigChangeSetCallback callback) {
+
+        }
+
+        @Override
         public Map<String, String> getProperties() {
             return Collections.emptyMap();
         }
 
         @Override
         public String toString(){
-            return "PropertySource [name=<empty>]";
+            return "Configuration [name=<empty>]";
         }
     };
 

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/api/src/main/java/org/apache/tamaya/PropertySource.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertySource.java b/api/src/main/java/org/apache/tamaya/PropertySource.java
index f4b01ae..152411a 100644
--- a/api/src/main/java/org/apache/tamaya/PropertySource.java
+++ b/api/src/main/java/org/apache/tamaya/PropertySource.java
@@ -18,7 +18,10 @@
 */
 package org.apache.tamaya;
 
+import org.apache.tamaya.spi.ConfigChangeSetCallback;
+
 import java.util.*;
+import java.util.concurrent.Callable;
 import java.util.function.Function;
 import java.util.function.UnaryOperator;
 
@@ -58,6 +61,21 @@ public interface PropertySource extends PropertyMapSupplier {
         }
 
         @Override
+        public void update(ConfigChangeSet changeSet) {
+
+        }
+
+        @Override
+        public void registerForUpdate(ConfigChangeSetCallback callback) {
+
+        }
+
+        @Override
+        public void removeForUpdate(ConfigChangeSetCallback callback) {
+
+        }
+
+        @Override
         public Map<String, String> getProperties() {
             return Collections.emptyMap();
         }
@@ -125,5 +143,25 @@ public interface PropertySource extends PropertyMapSupplier {
         return query.apply(this);
     }
 
+    /**
+     * Upon receiving a ConfigChangeSet, the PropertySource will be updated to include
+     * any of the listed changes within
+     *
+     * @param changeSet the changes to be invoked
+     */
+    void update(ConfigChangeSet changeSet);
+
+    /**
+     * Whenever this PropertySource is updated, any registered callables will be invoked
+     * @param callback
+     */
+    void registerForUpdate(ConfigChangeSetCallback callback);
+
+    /**
+     * Removes a callback to be invoked.
+     * @param callback
+     */
+    void removeForUpdate(ConfigChangeSetCallback callback);
+
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/api/src/main/java/org/apache/tamaya/spi/ConfigChangeSetCallback.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/ConfigChangeSetCallback.java b/api/src/main/java/org/apache/tamaya/spi/ConfigChangeSetCallback.java
new file mode 100644
index 0000000..889b8c4
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/spi/ConfigChangeSetCallback.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.spi;
+
+import org.apache.tamaya.ConfigChangeSet;
+
+/**
+ * A callback to be notified whenever a change comes in
+ */
+@FunctionalInterface
+public interface ConfigChangeSetCallback {
+    /**
+     * Method to be invoked on change.  Includes the ConfigChangeSet.
+     * @param changeSet
+     */
+    public void onChange(ConfigChangeSet changeSet);
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/api/src/main/java/org/apache/tamaya/spi/ConfigurationFactorySpi.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/ConfigurationFactorySpi.java b/api/src/main/java/org/apache/tamaya/spi/ConfigurationFactorySpi.java
index 3b167cc..a0a91d3 100644
--- a/api/src/main/java/org/apache/tamaya/spi/ConfigurationFactorySpi.java
+++ b/api/src/main/java/org/apache/tamaya/spi/ConfigurationFactorySpi.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tamaya.spi;
 
+import org.apache.tamaya.ConfigChangeSet;
 import org.apache.tamaya.Configuration;
 import org.apache.tamaya.PropertySource;
 
@@ -47,6 +48,21 @@ public interface ConfigurationFactorySpi {
             }
 
             @Override
+            public void update(ConfigChangeSet changeSet) {
+                propertySource.update(changeSet);
+            }
+
+            @Override
+            public void registerForUpdate(ConfigChangeSetCallback callback) {
+                propertySource.registerForUpdate(callback);
+            }
+
+            @Override
+            public void removeForUpdate(ConfigChangeSetCallback callback) {
+                propertySource.removeForUpdate(callback);
+            }
+
+            @Override
             public Map<String, String> getProperties() {
                 return propertySource.getProperties();
             }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSet.java b/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSet.java
deleted file mode 100644
index 7ef3477..0000000
--- a/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSet.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.config;
-
-import org.apache.tamaya.PropertySource;
-
-import java.beans.PropertyChangeEvent;
-import java.io.Serializable;
-import java.util.*;
-
-/**
- * Event that contains a set current changes that were applied or could be applied.
- * This class is immutable and thread-safe. To create instances use
- * {@link ConfigChangeSetBuilder}.
- *
- * Created by Anatole on 22.10.2014.
- */
-public final class ConfigChangeSet implements Serializable{
-
-    private static final long serialVersionUID = 1l;
-    /** The base property provider/configuration. */
-    private PropertySource propertySource;
-    /** The base version, usable for optimistic locking. */
-    private String baseVersion;
-    /** The recorded changes. */
-    private Map<String,PropertyChangeEvent> changes = new HashMap<>();
-
-    /**
-     * Get an empty change set for the given provider.
-     * @param propertyProvider The base property provider/configuration, not null.
-     * @return an empty ConfigChangeSet instance.
-     */
-    public static ConfigChangeSet emptyChangeSet(PropertySource propertyProvider){
-        return new ConfigChangeSet(propertyProvider, Collections.emptySet());
-    }
-
-    /**
-     * Constructor used by {@link ConfigChangeSetBuilder}.
-     * @param propertySource The base property provider/configuration, not null.
-     * @param changes The recorded changes, not null.
-     */
-    ConfigChangeSet(PropertySource propertySource, Collection<PropertyChangeEvent> changes) {
-        this.propertySource = Objects.requireNonNull(propertySource);
-        changes.forEach((c) -> this.changes.put(c.getPropertyName(), c));
-    }
-
-    /**
-     * Get the underlying property provider/configuration.
-     * @return the underlying property provider/configuration, never null.
-     */
-    public PropertySource getPropertySource(){
-        return this.propertySource;
-    }
-
-    /**
-     * Get the base version, usable for optimistic locking.
-     * @return the base version.
-     */
-    public String getBaseVersion(){
-        return baseVersion;
-    }
-
-    /**
-     * Get the changes recorded.
-     * @return the recorded changes, never null.
-     */
-    public Collection<PropertyChangeEvent> getEvents(){
-        return Collections.unmodifiableCollection(this.changes.values());
-    }
-
-    /**
-     * Access the number current removed entries.
-     * @return the number current removed entries.
-     */
-    public int getRemovedSize() {
-        return (int) this.changes.values().stream().filter((e) -> e.getNewValue() == null).count();
-    }
-
-    /**
-     * Access the number current added entries.
-     * @return the number current added entries.
-     */
-    public int getAddedSize() {
-        return (int) this.changes.values().stream().filter((e) -> e.getOldValue() == null).count();
-    }
-
-    /**
-     * Access the number current updated entries.
-     * @return the number current updated entries.
-     */
-    public int getUpdatedSize() {
-        return (int) this.changes.values().stream().filter((e) -> e.getOldValue()!=null && e.getNewValue()!=null).count();
-    }
-
-
-    /**
-     * Checks if the given key was removed.
-     * @param key the target key, not null.
-     * @return true, if the given key was removed.
-     */
-    public boolean isRemoved(String key) {
-        PropertyChangeEvent change = this.changes.get(key);
-        return change != null && change.getNewValue() == null;
-    }
-
-    /**
-     * Checks if the given key was added.
-     * @param key the target key, not null.
-     * @return true, if the given key was added.
-     */
-    public boolean isAdded(String key) {
-        PropertyChangeEvent change = this.changes.get(key);
-        return change != null && change.getOldValue() == null;
-    }
-
-    /**
-     * Checks if the given key was updated.
-     * @param key the target key, not null.
-     * @return true, if the given key was updated.
-     */
-    public boolean isUpdated(String key) {
-        PropertyChangeEvent change = this.changes.get(key);
-        return change != null && change.getOldValue() != null && change.getNewValue() != null;
-    }
-
-    /**
-     * Checks if the given key is added, or updated AND NOT removed.
-     * @param key the target key, not null.
-     * @return true, if the given key was added, or updated BUT NOT removed.
-     */
-    public boolean containsKey(String key) {
-        PropertyChangeEvent change = this.changes.get(key);
-        return change != null && change.getNewValue() != null;
-    }
-
-    /**
-     * CHecks if the current change set does not contain any changes.
-     * @return tru, if the change set is empty.
-     */
-    public boolean isEmpty(){
-        return this.changes.isEmpty();
-    }
-
-
-    @Override
-    public String toString() {
-        return "ConfigChangeSet{" +
-                "properties=" + propertySource +
-                ", baseVersion=" + baseVersion +
-                ", changes=" + changes +
-                '}';
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSetBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSetBuilder.java b/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSetBuilder.java
deleted file mode 100644
index 0b536e0..0000000
--- a/core/src/main/java/org/apache/tamaya/core/config/ConfigChangeSetBuilder.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.config;
-
-import org.apache.tamaya.Configuration;
-import org.apache.tamaya.PropertySource;
-
-import java.beans.PropertyChangeEvent;
-import java.util.*;
-import java.util.function.Function;
-
-/**
- * Models a set current changes to be applied to a configuration/property provider.  Such a set can be applied
- * to any {@link org.apache.tamaya.PropertySource} instance. If the provider is mutable it may check the
- * version given and applyChanges the changes to the provider/configuration, including triggering current regarding
- * change events.
- * <p>
- * For appropriate conversion a {@code Function<String, Codec>} can be applied, which performs correct conversion,
- * when changed values are set. This function enables connecting e.g. setters on a configuration template with
- * the corresponding conversion logic, so the template calls are correctly converted back.
- */
-public final class ConfigChangeSetBuilder {
-    /**
-     * The recorded changes.
-     */
-    final SortedMap<String, PropertyChangeEvent> delta = new TreeMap<>();
-    /**
-     * The underlying configuration/provider.
-     */
-    PropertySource source;
-
-    /**
-     * Constructor.
-     *
-     * @param source      the underlying configuration/provider, not null.
-     */
-    private ConfigChangeSetBuilder(PropertySource source) {
-        this.source = Objects.requireNonNull(source);
-    }
-
-    /**
-     * Creates a new instance current this builder.
-     *
-     * @param source the underlying property provider/configuration, not null.
-     * @return the builder for chaining.
-     */
-    public static ConfigChangeSetBuilder of(PropertySource source) {
-        return new ConfigChangeSetBuilder(source);
-    }
-
-
-    /**
-     * Creates a new instance current this builder.
-     *
-     * @param configuration the base configuration, not null.
-     * @return the builder for chaining.
-     */
-    public static ConfigChangeSetBuilder of(Configuration configuration) {
-        return new ConfigChangeSetBuilder(configuration);
-    }
-
-    /**
-     * This method records all changes to be applied to the base property provider/configuration to
-     * achieve the given target state.
-     *
-     * @param newState the new target state, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder addChanges(PropertySource newState) {
-        compare(newState, this.source).forEach((c) -> this.delta.put(c.getPropertyName(), c));
-        return this;
-    }
-
-    /**
-     * Get the current values, also considering any changes recorded within this change set.
-     *
-     * @param key the key current the entry, not null.
-     * @return the keys, or null.
-     */
-    public String get(String key) {
-        PropertyChangeEvent change = this.delta.get(key);
-        if (change != null && !(change.getNewValue() == null)) {
-            return (String) change.getNewValue();
-        }
-        return null;
-    }
-
-    /**
-     * Marks the given key(s) fromMap the configuration/properties to be removed.
-     *
-     * @param key       the key current the entry, not null.
-     * @param otherKeys additional keys to be removed (convenience), not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder remove(String key, String... otherKeys) {
-        String oldValue = this.source.get(key).orElse(null);
-        if (oldValue == null) {
-            this.delta.remove(key);
-        }
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, oldValue, null));
-        for (String addKey : otherKeys) {
-            oldValue = this.source.get(addKey).orElse(null);
-            if (oldValue == null) {
-                this.delta.remove(addKey);
-            }
-            this.delta.put(addKey, new PropertyChangeEvent(this.source, addKey, oldValue, null));
-        }
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, boolean value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     s* Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, byte value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, char value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, short value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, int value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, long value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, float value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, double value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder put(String key, String value) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), String.valueOf(value)));
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @return the builder for chaining.
-     * @throws org.apache.tamaya.ConfigException if no matching Codec could be found.
-     */
-    public <T> ConfigChangeSetBuilder put(String key, Class<T> type, T value) {
-        put(key, type, value, null);
-        return this;
-    }
-
-    /**
-     * Applies the given keys.
-     *
-     * @param key   the key current the entry, not null.
-     * @param value the keys to be applied, not null.
-     * @param adapter the codec to be used, if set overrides any other codecs that may apply. If null an appropriate
-     *              codec is tried to be evaluated as needed.
-     * @return the builder for chaining.
-     * @throws org.apache.tamaya.ConfigException if no matching Codec could be found.
-     */
-    public <T> ConfigChangeSetBuilder put(String key, Class<T> type, T value, Function<T,String> adapter) {
-        this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), adapter.apply(Objects.requireNonNull(value))));
-        return this;
-    }
-
-
-    /**
-     * Apply all the given values to the base configuration/properties.
-     * Note that all values passed must be convertible to String, either
-     * <ul>
-     * <li>the registered codecs provider provides codecs for the corresponding keys, or </li>
-     * <li>default codecs are present for the given type, or</li>
-     * <li>the value is an instanceof String</li>
-     * </ul>
-     *
-     * @param changes the changes to be applied, not null.
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder putAll(Map<String, String> changes) {
-        changes.putAll(changes);
-        return this;
-    }
-
-    /**
-     * This method will create a change set that clears all entries fromMap the given base configuration/properties.
-     *
-     * @return the builder for chaining.
-     */
-    public ConfigChangeSetBuilder deleteAll() {
-        this.delta.clear();
-        this.source.getProperties().forEach((k, v) ->
-                this.delta.put(k, new PropertyChangeEvent(this.source, k, v, null)));
-        return this;
-    }
-
-    /**
-     * Checks if the change set is empty, i.e. does not contain any changes.
-     *
-     * @return true, if the set is empty.
-     */
-    public boolean isEmpty() {
-        return this.delta.isEmpty();
-    }
-
-    /**
-     * Resets this change set instance. This will clear all changes done to this builder, so the
-     * set will be empty.
-     */
-    public void reset() {
-        this.delta.clear();
-    }
-
-    /**
-     * Builds the corresponding change set.
-     *
-     * @return the new change set, never null.
-     */
-    public ConfigChangeSet build() {
-        return new ConfigChangeSet(this.source, Collections.unmodifiableCollection(this.delta.values()));
-    }
-
-    /**
-     * Compares the two property config/configurations and creates a collection current all changes
-     * that must be appied to render {@code map1} into {@code map2}.
-     *
-     * @param map1 the source map, not null.
-     * @param map2 the target map, not null.
-     * @return a collection current change events, never null.
-     */
-    public static Collection<PropertyChangeEvent> compare(PropertySource map1, PropertySource map2) {
-        List<PropertyChangeEvent> changes = new ArrayList<>();
-        for (Map.Entry<String, String> en : map1.getProperties().entrySet()) {
-            Optional<String> val = map2.get(en.getKey());
-            if (!val.isPresent()) {
-                changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue()));
-            } else if (!val.get().equals(en.getValue())) {
-                changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue()));
-            }
-        }
-        for (Map.Entry<String, String> en : map2.getProperties().entrySet()) {
-            Optional<String> val = map1.get(en.getKey());
-            if (!val.isPresent()) {
-                changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue()));
-            } else if (!val.equals(Optional.ofNullable(en.getValue()))) {
-                changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue()));
-            }
-        }
-        return changes;
-    }
-
-    /*
-     * (non-Javadoc)
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return "PropertyChangeEventBuilder [source=" + source + ", " +
-                ", delta=" + delta + "]";
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a1777439/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java b/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
index fbfd6df..6564e5b 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
@@ -21,7 +21,9 @@ package org.apache.tamaya.core.properties;
 import java.io.Serializable;
 import java.util.*;
 
+import org.apache.tamaya.ConfigChangeSet;
 import org.apache.tamaya.PropertySource;
+import org.apache.tamaya.spi.ConfigChangeSetCallback;
 
 /**
  * Abstract base class for implementing a {@link org.apache.tamaya.PropertySource}.
@@ -39,6 +41,8 @@ public abstract class AbstractPropertySource implements PropertySource, Serializ
      */
     private volatile Set<String> sources = new HashSet<>();
 
+    private volatile Set<ConfigChangeSetCallback> callbacks = new LinkedHashSet<>();
+
     /**
      * Constructor.
      */
@@ -82,6 +86,22 @@ public abstract class AbstractPropertySource implements PropertySource, Serializ
         return b.append('}').toString();
     }
 
+    @Override
+    public void update(ConfigChangeSet changeSet) {
+        //TODO how do we want to update this guy?
+        this.callbacks.parallelStream().forEach((c) -> c.onChange(changeSet));
+    }
+
+    @Override
+    public void registerForUpdate(ConfigChangeSetCallback callback) {
+        this.callbacks.add(callback);
+    }
+
+    @Override
+    public void removeForUpdate(ConfigChangeSetCallback callback) {
+        this.callbacks.remove(callback);
+    }
+
     protected String printContents(StringBuilder b){
         Map<String,String> sortMap = getProperties();
         if(!(sortMap instanceof SortedMap)){