TAMAYA-175 Moved the events module to the directory events after extracting it 
for the Tamaya main repository.


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

Branch: refs/heads/master
Commit: 89223dcd49f9045f01ed04b803add7cabe746cbd
Parents: 9bf98fe
Author: Oliver B. Fischer <ple...@apache.org>
Authored: Tue Sep 27 00:17:11 2016 +0200
Committer: Oliver B. Fischer <ple...@apache.org>
Committed: Fri Sep 30 21:29:37 2016 +0200

----------------------------------------------------------------------
 events/pom.xml                                  | 100 +++++++
 .../org/apache/tamaya/events/ChangeType.java    |  31 +++
 .../org/apache/tamaya/events/ConfigEvent.java   |  55 ++++
 .../tamaya/events/ConfigEventListener.java      |  31 +++
 .../tamaya/events/ConfigEventManager.java       | 186 +++++++++++++
 .../tamaya/events/ConfigurationChange.java      | 218 +++++++++++++++
 .../events/ConfigurationChangeBuilder.java      | 274 +++++++++++++++++++
 .../events/ConfigurationContextChange.java      | 210 ++++++++++++++
 .../ConfigurationContextChangeBuilder.java      | 174 ++++++++++++
 .../tamaya/events/FrozenConfiguration.java      | 194 +++++++++++++
 .../tamaya/events/FrozenPropertySource.java     | 126 +++++++++
 .../tamaya/events/PropertySourceChange.java     | 242 ++++++++++++++++
 .../events/PropertySourceChangeBuilder.java     | 263 ++++++++++++++++++
 .../tamaya/events/delta/package-info.java       |  23 ++
 .../folderobserver/FileChangeListener.java      | 144 ++++++++++
 .../folderobserver/FileChangeObserver.java      |  33 +++
 .../ObservingPropertySourceProvider.java        | 209 ++++++++++++++
 .../events/folderobserver/package-info.java     |  24 ++
 .../internal/DefaultConfigChangeObserver.java   | 111 ++++++++
 .../internal/DefaultConfigEventManagerSpi.java  | 202 ++++++++++++++
 ...faultConfigurationContextChangeListener.java |  74 +++++
 .../events/internal/LoggingConfigListener.java  |  40 +++
 .../tamaya/events/internal/package-info.java    |  22 ++
 .../org/apache/tamaya/events/package-info.java  |  24 ++
 .../tamaya/events/spi/BaseConfigEvent.java      |  69 +++++
 .../events/spi/ConfigEventManagerSpi.java       | 128 +++++++++
 .../apache/tamaya/events/spi/package-info.java  |  23 ++
 ...org.apache.tamaya.events.ConfigEventListener |  19 ++
 ...ache.tamaya.events.spi.ConfigEventManagerSpi |  19 ++
 .../events/ChangeableGlobalPropertySource.java  |  62 +++++
 .../ChangeableThreadLocalPropertySource.java    |  57 ++++
 .../tamaya/events/ConfigEventManagerTest.java   |  66 +++++
 .../tamaya/events/FrozenPropertySourceTest.java | 106 +++++++
 .../tamaya/events/ObservedConfigTest.java       |  69 +++++
 .../tamaya/events/RandomPropertySource.java     |  66 +++++
 .../org/apache/tamaya/events/SimpleEvent.java   |  32 +++
 .../apache/tamaya/events/TestConfigView.java    | 156 +++++++++++
 .../tamaya/events/TestObservingProvider.java    |  92 +++++++
 .../events/delta/ConfigurationChangeTest.java   | 163 +++++++++++
 .../delta/ConfigurationContextChangeTest.java   | 138 ++++++++++
 .../events/delta/PropertySourceChangeTest.java  | 209 ++++++++++++++
 .../DefaultConfigEventManagerSpiTest.java       |  66 +++++
 ...org.apache.tamaya.events.ConfigEventListener |  19 ++
 .../org.apache.tamaya.spi.PropertySource        |  19 ++
 ...org.apache.tamaya.spi.PropertySourceProvider |  19 ++
 events/src/test/resources/data/test1.properties |  20 ++
 .../src/test/resources/data/test1b.properties   |  20 ++
 events/src/test/resources/data/test2.properties |  20 ++
 events/src/test/resources/data/test3.properties |  20 ++
 events/src/test/resources/test.properties       |  21 ++
 pom.xml                                         | 100 -------
 .../org/apache/tamaya/events/ChangeType.java    |  31 ---
 .../org/apache/tamaya/events/ConfigEvent.java   |  55 ----
 .../tamaya/events/ConfigEventListener.java      |  31 ---
 .../tamaya/events/ConfigEventManager.java       | 186 -------------
 .../tamaya/events/ConfigurationChange.java      | 218 ---------------
 .../events/ConfigurationChangeBuilder.java      | 274 -------------------
 .../events/ConfigurationContextChange.java      | 210 --------------
 .../ConfigurationContextChangeBuilder.java      | 174 ------------
 .../tamaya/events/FrozenConfiguration.java      | 194 -------------
 .../tamaya/events/FrozenPropertySource.java     | 126 ---------
 .../tamaya/events/PropertySourceChange.java     | 242 ----------------
 .../events/PropertySourceChangeBuilder.java     | 263 ------------------
 .../tamaya/events/delta/package-info.java       |  23 --
 .../folderobserver/FileChangeListener.java      | 144 ----------
 .../folderobserver/FileChangeObserver.java      |  33 ---
 .../ObservingPropertySourceProvider.java        | 209 --------------
 .../events/folderobserver/package-info.java     |  24 --
 .../internal/DefaultConfigChangeObserver.java   | 111 --------
 .../internal/DefaultConfigEventManagerSpi.java  | 202 --------------
 ...faultConfigurationContextChangeListener.java |  74 -----
 .../events/internal/LoggingConfigListener.java  |  40 ---
 .../tamaya/events/internal/package-info.java    |  22 --
 .../org/apache/tamaya/events/package-info.java  |  24 --
 .../tamaya/events/spi/BaseConfigEvent.java      |  69 -----
 .../events/spi/ConfigEventManagerSpi.java       | 128 ---------
 .../apache/tamaya/events/spi/package-info.java  |  23 --
 ...org.apache.tamaya.events.ConfigEventListener |  19 --
 ...ache.tamaya.events.spi.ConfigEventManagerSpi |  19 --
 .../events/ChangeableGlobalPropertySource.java  |  62 -----
 .../ChangeableThreadLocalPropertySource.java    |  57 ----
 .../tamaya/events/ConfigEventManagerTest.java   |  66 -----
 .../tamaya/events/FrozenPropertySourceTest.java | 106 -------
 .../tamaya/events/ObservedConfigTest.java       |  69 -----
 .../tamaya/events/RandomPropertySource.java     |  66 -----
 .../org/apache/tamaya/events/SimpleEvent.java   |  32 ---
 .../apache/tamaya/events/TestConfigView.java    | 156 -----------
 .../tamaya/events/TestObservingProvider.java    |  92 -------
 .../events/delta/ConfigurationChangeTest.java   | 163 -----------
 .../delta/ConfigurationContextChangeTest.java   | 138 ----------
 .../events/delta/PropertySourceChangeTest.java  | 209 --------------
 .../DefaultConfigEventManagerSpiTest.java       |  66 -----
 ...org.apache.tamaya.events.ConfigEventListener |  19 --
 .../org.apache.tamaya.spi.PropertySource        |  19 --
 ...org.apache.tamaya.spi.PropertySourceProvider |  19 --
 src/test/resources/data/test1.properties        |  20 --
 src/test/resources/data/test1b.properties       |  20 --
 src/test/resources/data/test2.properties        |  20 --
 src/test/resources/data/test3.properties        |  20 --
 src/test/resources/test.properties              |  21 --
 100 files changed, 4708 insertions(+), 4708 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/pom.xml
----------------------------------------------------------------------
diff --git a/events/pom.xml b/events/pom.xml
new file mode 100644
index 0000000..d274633
--- /dev/null
+++ b/events/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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 current 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.tamaya.ext</groupId>
+        <artifactId>tamaya-extensions</artifactId>
+        <version>0.3-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>tamaya-events</artifactId>
+    <name>Apache Tamaya Modules - Event and dynamic Update Extensions</name>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <jdkVersion>1.7</jdkVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.tamaya</groupId>
+            <artifactId>tamaya-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya.ext</groupId>
+            <artifactId>tamaya-functions</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya.ext</groupId>
+            <artifactId>tamaya-spisupport</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya</groupId>
+            <artifactId>tamaya-core</artifactId>
+            <version>${project.version}</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>java-hamcrest</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.tamaya.events,
+                            org.apache.tamaya.events.delta,
+                            org.apache.tamaya.events.folderobserver,
+                            org.apache.tamaya.events.spi
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.tamaya.events.internal
+                        </Private-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ChangeType.java
----------------------------------------------------------------------
diff --git a/events/src/main/java/org/apache/tamaya/events/ChangeType.java 
b/events/src/main/java/org/apache/tamaya/events/ChangeType.java
new file mode 100644
index 0000000..2059017
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/ChangeType.java
@@ -0,0 +1,31 @@
+/*
+ * 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.events;
+
+/**
+ * Enum describing the type of configuration change.
+ */
+public enum ChangeType {
+    /** Configuration hase been added. */
+    NEW,
+    /** Configuration hase been removed. */
+    DELETED,
+    /** Configuration hase been changed. */
+    UPDATED,
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ConfigEvent.java
----------------------------------------------------------------------
diff --git a/events/src/main/java/org/apache/tamaya/events/ConfigEvent.java 
b/events/src/main/java/org/apache/tamaya/events/ConfigEvent.java
new file mode 100644
index 0000000..5a713d7
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/ConfigEvent.java
@@ -0,0 +1,55 @@
+/*
+ * 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.events;
+
+
+/**
+ * Event that contains a set current changes that were applied or could be 
applied.
+ * @param <T> the resource type.
+ */
+public interface ConfigEvent<T>{
+
+    /**
+     * Access the type of resource. This allows to easily determine the 
resource an event wants to observe.
+     * @return the resource type.
+     */
+    Class<T> getResourceType();
+
+    /**
+     * Get the underlying property provider/configuration.
+     * @return the underlying property provider/configuration, never null.
+     */
+    T getResource();
+
+    /**
+     * Get the version relative to the observed resource. The version is 
required to be unique for
+     * each change emmitted for a resource. There is no further requirement 
how this uniqueness is
+     * modelled, so returning a UUID is a completely valid strategy.
+     * @return the base version.
+     */
+    String getVersion();
+
+    /**
+     * Get the timestamp in millis from the current epoch. it is expected that 
the timestamp and the version are unique to
+     * identify a changeset.
+     * @return the timestamp, when this changeset was created.
+     */
+    long getTimestamp();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ConfigEventListener.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/ConfigEventListener.java 
b/events/src/main/java/org/apache/tamaya/events/ConfigEventListener.java
new file mode 100644
index 0000000..7fb32c8
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/ConfigEventListener.java
@@ -0,0 +1,31 @@
+/*
+ * 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.events;
+
+/**
+ * Interface to be implemented for listening on changes on {@link 
org.apache.tamaya.Configuration} instances.
+ */
+public interface ConfigEventListener {
+    /**
+     * Called if an event occurred.
+     * @param event the event, not null.
+     */
+    void onConfigEvent(ConfigEvent<?> event);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java 
b/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java
new file mode 100644
index 0000000..f6bd3da
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java
@@ -0,0 +1,186 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.events.spi.ConfigEventManagerSpi;
+import org.apache.tamaya.spi.ServiceContextManager;
+
+import java.util.Collection;
+
+/**
+ * Singleton accessor for accessing the event support component that 
distributes change events of
+ * {@link org.apache.tamaya.spi.PropertySource} and {@link 
org.apache.tamaya.Configuration}.
+ */
+public final class ConfigEventManager {
+    /**
+     * The backing SPI.
+     */
+    private static final ConfigEventManagerSpi SPI = 
ServiceContextManager.getServiceContext()
+            .getService(ConfigEventManagerSpi.class);
+
+    /**
+     * Private singleton constructor.
+     */
+    private ConfigEventManager() {
+    }
+
+    /**
+     * Adds a Config listener that listens to all kind of {@link ConfigEvent}.
+     * @param l the listener not null.
+     */
+    public static void addListener(ConfigEventListener l) {
+        if (SPI == null) {
+            throw new ConfigException("No SPI registered for " +
+                    ConfigEventManager.class.getName());
+        }
+        SPI.addListener(l);
+    }
+
+    /**
+     * Adds a Config listener that listens to all kind of {@link ConfigEvent}.
+     * @param <T> the type of the event.
+     * @param l the listener not null.
+     * @param eventType the event type to which this listener listens to.
+     */
+    public static <T extends ConfigEvent> void addListener(ConfigEventListener 
l, Class<T> eventType) {
+        if (SPI == null) {
+            throw new ConfigException("No SPI registered for " +
+                    ConfigEventManager.class.getName());
+        }
+        SPI.addListener(l);
+    }
+
+    /**
+     * Removes a listener registered globally.
+     *
+     * @param l the listener not null.
+     */
+    public static void removeListener(ConfigEventListener l) {
+        if (SPI == null) {
+            throw new ConfigException("No SPI registered for " +
+                    ConfigEventManager.class.getName());
+        }
+        SPI.removeListener(l);
+    }
+
+    /**
+     * Removes a listener registered for the given event type.
+     *
+     * @param <T> the type of the event.
+     * @param l the listener, not null.
+     * @param eventType the event type to which this listener listens to.
+     */
+    public static <T extends ConfigEvent> void 
removeListener(ConfigEventListener l, Class<T> eventType) {
+        if (SPI == null) {
+            throw new ConfigException("No SPI registered for " +
+                    ConfigEventManager.class.getName());
+        }
+        SPI.removeListener(l);
+    }
+
+    /**
+     * Access all registered ConfigEventListeners listening to a given event 
type.
+     * @param type the event type
+     * @param <T> type param
+     * @return a list with the listeners found, never null.
+     */
+    public static <T extends ConfigEvent>
+        Collection<? extends ConfigEventListener> getListeners(Class<T> type) {
+        return SPI.getListeners(type);
+    }
+
+    /**
+     * Access all registered ConfigEventListeners listening to a all kind of 
event types globally.
+     * 
+     * @param <T> the type of the event.
+     * @return a list with the listeners found, never null.
+     */
+    public static <T extends ConfigEvent>
+    Collection<? extends ConfigEventListener> getListeners() {
+        return SPI.getListeners();
+    }
+
+    /**
+     * Publishes a {@link ConfigurationChange} synchronously to all interested 
listeners.
+     * 
+     * @param <T> the type of the event.
+     * @param event the event, not null.
+     */
+    public static <T> void fireEvent(ConfigEvent<?> event) {
+        SPI.fireEvent(event);
+    }
+
+    /**
+     * Publishes a {@link ConfigurationChange} asynchronously/multithreaded to 
all interested listeners.
+     *
+     * @param <T> the type of the event.
+     * @param event the event, not null.
+     */
+    public static <T> void fireEventAsynch(ConfigEvent<?> event) {
+        SPI.fireEventAsynch(event);
+    }
+
+    /**
+     * Start/Stop the change monitoring service, which will observe/reevaluate 
the current configuration regularly
+     * and trigger ConfigurationChange events if something changed. This is 
quite handy for publishing
+     * configuration changes to whatever systems are interested in. Hereby the 
origin of a configuration change
+     * can be on this machine, or also remotely. For handling corresponding 
{@link ConfigEventListener} have
+     * to be registered, e.g. listening on {@link 
org.apache.tamaya.events.ConfigurationChange} events.
+     * 
+     * @param enable whether to enable or disable the change monitoring.
+     * 
+     * @see #isChangeMonitoring()
+     * @see #getChangeMonitoringPeriod()
+     */
+    public static void enableChangeMonitoring(boolean enable) {
+        SPI.enableChangeMonitor(enable);
+    }
+
+    /**
+     * Check if the observer is running currently.
+     *
+     * @return true, if the change monitoring service is currently running.
+     * @see #enableChangeMonitoring(boolean)
+     */
+    public static boolean isChangeMonitoring() {
+        return SPI.isChangeMonitorActive();
+    }
+
+    /**
+     * Get the current check period to check for configuration changes.
+     *
+     * @return the check period in ms.
+     */
+    public static long getChangeMonitoringPeriod(){
+        return SPI.getChangeMonitoringPeriod();
+    }
+
+    /**
+     * Sets the current monitoring period and restarts the monitor. You still 
have to enable the monitor if
+     * it is currently not enabled.
+     * @param millis the monitoring period in ms.
+     * @see #enableChangeMonitoring(boolean)
+     * @see #isChangeMonitoring()
+     */
+    public static void setChangeMonitoringPeriod(long millis){
+        SPI.setChangeMonitoringPeriod(millis);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java 
b/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java
new file mode 100644
index 0000000..c31cda2
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java
@@ -0,0 +1,218 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.Configuration;
+
+import java.beans.PropertyChangeEvent;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * 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 PropertySourceChangeBuilder}.
+ *
+ * Created by Anatole on 22.10.2014.
+ */
+public final class ConfigurationChange implements ConfigEvent<Configuration>, 
Serializable{
+
+    private static final long serialVersionUID = 1L;
+    /** The base property provider/configuration. */
+    private final FrozenConfiguration configuration;
+    /** The base version, usable for optimistic locking. */
+    private String version = UUID.randomUUID().toString();
+    /** The timestamp of the change set in millis from the epoch. */
+    private long timestamp = System.currentTimeMillis();
+    /** The recorded changes. */
+    private final Map<String,PropertyChangeEvent> changes = new HashMap<>();
+
+    /**
+     * Get an empty change set for the given provider.
+     * @param configuration The configuration changed, not null.
+     * @return an empty ConfigurationChangeSet instance.
+     */
+    public static ConfigurationChange emptyChangeSet(Configuration 
configuration){
+        return ConfigurationChangeBuilder.of(configuration).build();
+    }
+
+    /**
+     * Constructor used by {@link PropertySourceChangeBuilder}.
+     * @param builder The builder used, not null.
+     */
+    ConfigurationChange(ConfigurationChangeBuilder builder) {
+        this.configuration = FrozenConfiguration.of(builder.source);
+        for(PropertyChangeEvent ev:builder.delta.values()){
+            this.changes.put(ev.getPropertyName(), ev);
+        }
+        if(builder.version!=null){
+            this.version = builder.version;
+        }
+        if(builder.timestamp!=null){
+            this.timestamp = builder.timestamp;
+        }
+    }
+
+    @Override
+    public Class<Configuration> getResourceType() {
+        return Configuration.class;
+    }
+
+    /**
+     * Get the underlying property provider/configuration.
+     * @return the underlying property provider/configuration, never null.
+     */
+    @Override
+    public Configuration getResource(){
+        return this.configuration;
+    }
+
+    /**
+     * Get the base version, usable for optimistic locking.
+     * @return the base version.
+     */
+    @Override
+    public String getVersion(){
+        return version;
+    }
+
+    /**
+     * Get the timestamp in millis from the current epoch. it is expected that 
the timestamp and the version are unique to
+     * identify a changeset.
+     * @return the timestamp, when this changeset was created.
+     */
+    @Override
+    public long getTimestamp(){
+        return timestamp;
+    }
+
+    /**
+     * Get the changes recorded.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertyChangeEvent> getChanges(){
+        return Collections.unmodifiableCollection(this.changes.values());
+    }
+
+    /**
+     * Access the number current removed entries.
+     * @return the number current removed entries.
+     */
+    public int getRemovedSize() {
+        int removedCount = 0;
+        for(PropertyChangeEvent ev:this.changes.values()){
+            if(ev.getNewValue() == null){
+                removedCount++;
+            }
+        }
+        return removedCount;
+    }
+
+    /**
+     * Access the number current added entries.
+     * @return the number current added entries.
+     */
+    public int getAddedSize() {
+        int addedCount = 0;
+        for(PropertyChangeEvent ev:this.changes.values()){
+            if(ev.getOldValue() == null &&
+                    ev.getNewValue() != null){
+                addedCount++;
+            }
+        }
+        return addedCount;
+    }
+
+    /**
+     * Access the number current updated entries.
+     * @return the number current updated entries.
+     */
+    public int getUpdatedSize() {
+        int updatedCount = 0;
+        for(PropertyChangeEvent ev:this.changes.values()){
+            if( ev.getOldValue()!=null && ev.getNewValue()!=null){
+                updatedCount++;
+            }
+        }
+        return updatedCount;
+    }
+
+
+    /**
+     * 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 isKeyAffected(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 "ConfigurationChange{" +
+                "configuration=" + configuration +
+                ", version='" + version + '\'' +
+                ", timestamp=" + timestamp +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java 
b/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java
new file mode 100644
index 0000000..1fd228a
--- /dev/null
+++ 
b/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java
@@ -0,0 +1,274 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.ConfigurationProvider;
+
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Models a set current changes applied to a {@link 
org.apache.tamaya.spi.PropertySource}. Consumers of these events
+ * can observing changes to property sources and
+ * <ol>
+ * <li>Check if their current configuration instance ({@link 
org.apache.tamaya.spi.ConfigurationContext}
+ * contains the changed {@link org.apache.tamaya.spi.PropertySource} (Note: 
the reference tova property source is never affected by a
+ * change, its only the data of the property source).</li>
+ * <li>If so corresponding action may be taken, such as reevaluating the 
configuration values (depending on
+ * the update policy) or reevaluating the complete {@link 
org.apache.tamaya.Configuration} to create a change
+ * event on configuration level.
+ * </ol>
+ */
+public final class ConfigurationChangeBuilder {
+    /**
+     * The recorded changes.
+     */
+    final SortedMap<String, PropertyChangeEvent> delta = new TreeMap<>();
+    /**
+     * The underlying configuration/provider.
+     */
+    final Configuration source;
+    /**
+     * The version configured, or null, for generating a default.
+     */
+    String version;
+    /**
+     * The optional timestamp in millis of this epoch.
+     */
+    Long timestamp;
+
+    /**
+     * Constructor.
+     *
+     * @param configuration the underlying configuration, not null.
+     */
+    private ConfigurationChangeBuilder(Configuration configuration) {
+        this.source = Objects.requireNonNull(configuration);
+    }
+
+    /**
+     * Creates a new instance current this builder using the current 
COnfiguration as root resource.
+     *
+     * @return the builder for chaining.
+     */
+    public static ConfigurationChangeBuilder of() {
+        return new 
ConfigurationChangeBuilder(ConfigurationProvider.getConfiguration());
+    }
+
+    /**
+     * Creates a new instance current this builder.
+     *
+     * @param configuration the configuration changed, not null.
+     * @return the builder for chaining.
+     */
+    public static ConfigurationChangeBuilder of(Configuration configuration) {
+        return new ConfigurationChangeBuilder(configuration);
+    }
+
+    /**
+     * 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(Configuration map1, 
Configuration map2) {
+        List<PropertyChangeEvent> changes = new ArrayList<>();
+        for (Map.Entry<String, String> en : map1.getProperties().entrySet()) {
+            String val = map2.get(en.getKey());
+            if (val == null) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), null, 
en.getValue()));
+            } else if (!val.equals(en.getValue())) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), val, 
en.getValue()));
+            }
+        }
+        for (Map.Entry<String, String> en : map2.getProperties().entrySet()) {
+            String val = map1.get(en.getKey());
+            if (val == null) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), null, 
en.getValue()));
+            } else if (!val.equals(en.getValue())) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), val, 
en.getValue()));
+            }
+        }
+        return changes;
+    }
+
+    /*
+     * Apply a version/UUID to the set being built.
+     * @param version the version to apply, or null, to let the system 
generate a version for you.
+     * @return the builder for chaining.
+     */
+    public ConfigurationChangeBuilder setVersion(String version) {
+        this.version = version;
+        return this;
+    }
+
+    /*
+     * Apply given timestamp to the set being built.
+     * @param version the version to apply, or null, to let the system 
generate a version for you.
+     * @return the builder for chaining.
+     */
+    public ConfigurationChangeBuilder setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+        return this;
+    }
+
+    /**
+     * 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 ConfigurationChangeBuilder addChanges(Configuration newState) {
+        for (PropertyChangeEvent c : compare(newState, this.source)) {
+            this.delta.put(c.getPropertyName(), c);
+        }
+        return this;
+    }
+
+    /**
+     * Applies a single key/value change.
+     *
+     * @param key   the changed key
+     * @param value the new value.
+     * @return this instance for chining.
+     */
+    public ConfigurationChangeBuilder addChange(String key, String value) {
+        this.delta.put(key, new PropertyChangeEvent(this.source, key, 
this.source.get(key), value));
+        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 ConfigurationChangeBuilder removeKey(String key, String... 
otherKeys) {
+        String oldValue = this.source.get(key);
+        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);
+            if (oldValue == null) {
+                this.delta.remove(addKey);
+            }
+            this.delta.put(addKey, new PropertyChangeEvent(this.source, 
addKey, oldValue, null));
+        }
+        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 ConfigurationChangeBuilder putAll(Map<String, String> changes) {
+        for (Map.Entry<String, String> en : changes.entrySet()) {
+            this.delta.put(en.getKey(), new PropertyChangeEvent(this.source, 
en.getKey(), null, en.getValue()));
+        }
+        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 ConfigurationChangeBuilder removeAllKeys() {
+        this.delta.clear();
+        for (Map.Entry<String, String> en : 
this.source.getProperties().entrySet()) {
+            this.delta.put(en.getKey(), new PropertyChangeEvent(this.source, 
en.getKey(), en.getValue(), null));
+        }
+//        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 ConfigurationChange build() {
+        return new ConfigurationChange(this);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "ConfigurationChangeSetBuilder [config=" + source + ", " +
+                ", delta=" + delta + "]";
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChange.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChange.java 
b/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChange.java
new file mode 100644
index 0000000..4e12d42
--- /dev/null
+++ 
b/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChange.java
@@ -0,0 +1,210 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.spi.ConfigurationContext;
+import org.apache.tamaya.spi.PropertySource;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Event that contains a set of current changes that were applied or can be 
applied.
+ * This class is immutable and thread-safe. To create instances use
+ * {@link PropertySourceChangeBuilder}.
+ *
+ * Created by Anatole on 22.10.2014.
+ */
+public final class ConfigurationContextChange implements 
ConfigEvent<ConfigurationContext>, Serializable{
+
+    private static final long serialVersionUID = 1L;
+    /** The base property provider/configuration. */
+    private final List<PropertySourceChange> changedPropertySources = new 
ArrayList<>();
+    /** The base version, usable for optimistic locking. */
+    private String version = UUID.randomUUID().toString();
+    /** The timestamp of the change set in millis from the epoch. */
+    private long timestamp = System.currentTimeMillis();
+    /** The configuration context. */
+    private final ConfigurationContext configurationContext;
+
+    /**
+     * Get an empty change set for the given provider.
+     * 
+     * @param configurationContext context to use for creating changesets.
+     * @return an empty ConfigurationContextChange instance.
+     */
+    public static ConfigurationContextChange 
emptyChangeSet(ConfigurationContext configurationContext){
+        return 
ConfigurationContextChangeBuilder.of(configurationContext).build();
+    }
+
+    /**
+     * Constructor used by {@link PropertySourceChangeBuilder}.
+     * @param builder The builder used, not null.
+     */
+    ConfigurationContextChange(ConfigurationContextChangeBuilder builder) {
+        this.changedPropertySources.addAll(builder.changedPropertySources);
+        if(builder.version!=null){
+            this.version = builder.version;
+        }
+        if(builder.timestamp!=null){
+            this.timestamp = builder.timestamp;
+        }
+        this.configurationContext = builder.configurationContext;
+    }
+
+    @Override
+    public Class<ConfigurationContext> getResourceType() {
+        return ConfigurationContext.class;
+    }
+
+    @Override
+    public ConfigurationContext getResource() {
+        return configurationContext;
+    }
+
+    /**
+     * Get the base version, usable for optimistic locking.
+     * @return the base version.
+     */
+    @Override
+    public String getVersion(){
+        return version;
+    }
+
+    /**
+     * Get the timestamp in millis from the current epoch. it is expected that 
the timestamp and the version are unique to
+     * identify a changeset.
+     * @return the timestamp, when this changeset was created.
+     */
+    @Override
+    public long getTimestamp(){
+        return timestamp;
+    }
+
+    /**
+     * Get the changes recorded.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertySourceChange> getPropertySourceChanges(){
+        return Collections.unmodifiableCollection(this.changedPropertySources);
+    }
+
+    /**
+     * Get the property source updates.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertySourceChange> getPropertySourceUpdates(){
+        List<PropertySourceChange> result = new ArrayList<>();
+        for (PropertySourceChange pc : this.changedPropertySources) {
+            if (pc.getChangeType() == ChangeType.UPDATED) {
+                result.add(pc);
+            }
+        }
+        return result;
+//        return 
Collections.unmodifiableCollection(this.changedPropertySources).stream()
+//                .filter(pc -> 
pc.getChangeType()==ChangeType.UPDATED).collect(Collectors.toList());
+    }
+
+    /**
+     * Get the property sources to be removed.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertySource> getRemovedPropertySources(){
+        List<PropertySource> result = new ArrayList<>();
+        for (PropertySourceChange pc : this.changedPropertySources) {
+            if (pc.getChangeType() == ChangeType.DELETED) {
+                result.add(pc.getResource());
+            }
+        }
+        return result;
+//        return getPropertySourceChanges().stream().filter(pc -> 
pc.getChangeType()==ChangeType.DELETED).
+//                map(ps -> 
ps.getPropertySource()).collect(Collectors.toList());
+    }
+
+    /**
+     * Get the property sources to be added.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertySource> getAddedPropertySources(){
+        List<PropertySource> result = new ArrayList<>();
+        for (PropertySourceChange pc : this.changedPropertySources) {
+            if (pc.getChangeType() == ChangeType.NEW) {
+                result.add(pc.getResource());
+            }
+        }
+        return result;
+//        return getPropertySourceChanges().stream().filter(pc -> 
pc.getChangeType()==ChangeType.NEW).
+//                map(ps -> 
ps.getPropertySource()).collect(Collectors.toList());
+    }
+
+    /**
+     * Get the property sources to be updated.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertySource> getUpdatedPropertySources(){
+        List<PropertySource> result = new ArrayList<>();
+        for (PropertySourceChange pc : this.changedPropertySources) {
+            if (pc.getChangeType() == ChangeType.UPDATED) {
+                result.add(pc.getResource());
+            }
+        }
+        return result;
+//        return getPropertySourceChanges().stream().filter(pc -> 
pc.getChangeType()==ChangeType.UPDATED).
+//                map(ps -> 
ps.getPropertySource()).collect(Collectors.toList());
+    }
+
+    /**
+     * Checks if the given propertySource is affected (added, changed or 
removed).
+     * @param propertySource the propertySource, not null.
+     * @return true, if the given propertySource ia affected.
+     */
+    public boolean isAffected(PropertySource propertySource) {
+        for (PropertySourceChange ps : this.changedPropertySources) {
+            if (ps.getResource() == propertySource ||
+                    
ps.getResource().getName().equals(propertySource.getName())) {
+                return true;
+            }
+        }
+        return false;
+//        return this.changedPropertySources.stream().filter(ps ->  
ps.getPropertySource()==propertySource ||
+//                
ps.getPropertySource().getName().equals(propertySource.getName())).findAny().isPresent();
+    }
+
+    /**
+     * CHecks if the current change set does not contain any changes.
+     * @return tru, if the change set is empty.
+     */
+    public boolean isEmpty(){
+        return this.changedPropertySources.isEmpty();
+    }
+
+
+    @Override
+    public String toString() {
+        return "ConfigurationContextChange{" +
+                "changedPropertySources=" + changedPropertySources +
+                ", version='" + version + '\'' +
+                ", timestamp=" + timestamp +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChangeBuilder.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChangeBuilder.java
 
b/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChangeBuilder.java
new file mode 100644
index 0000000..b586428
--- /dev/null
+++ 
b/events/src/main/java/org/apache/tamaya/events/ConfigurationContextChangeBuilder.java
@@ -0,0 +1,174 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.ConfigurationProvider;
+import org.apache.tamaya.spi.ConfigurationContext;
+import org.apache.tamaya.spi.PropertySource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Models a set of current changes applied to a {@link 
org.apache.tamaya.spi.PropertySource}. Consumers of these events
+ * can observe changes to property sources and
+ * <ol>
+ *     <li>check if their current configuration instance ({@link 
org.apache.tamaya.spi.ConfigurationContext}
+ *     contains the changed {@link org.apache.tamaya.spi.PropertySource} 
(Note: the reference to a property source is never affected by a
+ *     change, it is the data of the property source only).</li>
+ *     <li>if so, a corresponding action may be taken, such as reevaluating 
the configuration values (depending on
+ *     the update policy) or reevaluating the complete {@link 
org.apache.tamaya.Configuration} to create a change
+ *     event on configuration level.
+ * </ol>
+ */
+public final class ConfigurationContextChangeBuilder {
+    /**
+     * The recorded changes.
+     */
+    final List<PropertySourceChange> changedPropertySources = new 
ArrayList<>();
+    /**
+     * The version configured, or null, for generating a default.
+     */
+    String version;
+    /**
+     * The optional timestamp in millis of this epoch.
+     */
+    Long timestamp;
+
+    final ConfigurationContext configurationContext;
+
+    /**
+     * Constructor.
+     */
+    private ConfigurationContextChangeBuilder(ConfigurationContext 
configurationContext) {
+        this.configurationContext = 
Objects.requireNonNull(configurationContext);
+    }
+
+    /**
+     * Just creates a new ConfigurationContextBuilder using the current 
COnfigurationContext has root resource.
+     * @return a new ConfigurationContextBuilder, never null.
+     */
+    public static ConfigurationContextChangeBuilder of() {
+        return of(ConfigurationProvider.getConfigurationContext());
+    }
+
+    /**
+     * Creates a new instance current this builder.
+     *
+     * @param context context to use for creating changesets.
+     * @return the builder for chaining.
+     */
+    public static ConfigurationContextChangeBuilder of(ConfigurationContext 
context) {
+        return new ConfigurationContextChangeBuilder(context);
+    }
+
+    /**
+     * Apply a version/UUID to the set being built.
+     * @param version the version to apply, or null, to let the system 
generate a version for you.
+     * @return the builder for chaining.
+     */
+    public ConfigurationContextChangeBuilder setVersion(String version) {
+        this.version = version;
+        return this;
+    }
+
+    /**
+     * Apply given timestamp to the set being built.
+     * @param timestamp timestamp to set.
+     * @return the builder for chaining.
+     */
+    public ConfigurationContextChangeBuilder setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+        return this;
+    }
+
+    /**
+     * This method records all changes to be applied to the base property 
provider/configuration to
+     * achieve the given target state.
+     *
+     * @param propertySource the new target state, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigurationContextChangeBuilder newPropertySource(PropertySource 
propertySource) {
+        
this.changedPropertySources.add(PropertySourceChange.ofAdded(propertySource));
+        return this;
+    }
+
+    /**
+     * This method records all changes to be applied to the base property 
provider/configuration to
+     * achieve the given target state.
+     *
+     * @param propertySource the new target state, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigurationContextChangeBuilder 
removedPropertySource(PropertySource propertySource) {
+        
this.changedPropertySources.add(PropertySourceChange.ofDeleted(propertySource));
+        return this;
+    }
+
+    /**
+     * This method records all changes to be applied to the base property 
provider/configuration to
+     * achieve the given target state.
+     *
+     * @param propertySourceChange the change state, not null.
+     * @return the builder for chaining.
+     */
+    public ConfigurationContextChangeBuilder 
changedPropertySource(PropertySourceChange propertySourceChange) {
+        
this.changedPropertySources.add(Objects.requireNonNull(propertySourceChange));
+        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.changedPropertySources.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.changedPropertySources.clear();
+    }
+
+    /**
+     * Builds the corresponding change set.
+     *
+     * @return the new change set, never null.
+     */
+    public ConfigurationContextChange build() {
+        return new ConfigurationContextChange(this);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "ConfigurationContextChangeBuilder [propertySources=" + 
changedPropertySources + "]";
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java 
b/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java
new file mode 100644
index 0000000..304ddba
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java
@@ -0,0 +1,194 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.ConfigOperator;
+import org.apache.tamaya.ConfigQuery;
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.ConfigurationProvider;
+import org.apache.tamaya.TypeLiteral;
+import org.apache.tamaya.functions.ConfigurationFunctions;
+import org.apache.tamaya.spi.ConfigurationContext;
+import org.apache.tamaya.spi.ConversionContext;
+import org.apache.tamaya.spi.PropertyConverter;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * /**
+ * Configuration implementation that stores all current values of a given 
(possibly dynamic, contextual and non server
+ * capable instance) and is fully serializable. Note that hereby only the 
scannable key/value pairs are considered.
+ */
+public final class FrozenConfiguration implements Configuration, Serializable {
+    private static final long serialVersionUID = -6373137316556444171L;
+    /**
+     * The properties frozen.
+     */
+    private Map<String, String> properties = new HashMap<>();
+
+    /**
+     * Constructor.
+     *
+     * @param config The base configuration.
+     */
+    private FrozenConfiguration(Configuration config) {
+        this.properties.putAll(config.getProperties());
+        this.properties.put("[meta]frozenAt", 
String.valueOf(System.currentTimeMillis()));
+        this.properties = Collections.unmodifiableMap(this.properties);
+    }
+
+    /**
+     * Creates a new FrozenConfiguration instance based on a Configuration 
given.
+     *
+     * @param config the configuration to be frozen, not null.
+     * @return the frozen Configuration.
+     */
+    public static FrozenConfiguration of(Configuration config) {
+        if (config instanceof FrozenConfiguration) {
+            return (FrozenConfiguration) config;
+        }
+        return new FrozenConfiguration(config);
+    }
+
+    @Override
+    public String get(String key) {
+        return this.properties.get(key);
+    }
+
+    @Override
+    public String getOrDefault(String key, String defaultValue) {
+        String val = get(key);
+        if(val==null){
+            return defaultValue;
+        }
+        return val;
+    }
+
+    @Override
+    public <T> T getOrDefault(String key, Class<T> type, T defaultValue) {
+        T val = get(key, type);
+        if(val==null){
+            return defaultValue;
+        }
+        return val;
+    }
+
+    @Override
+    public <T> T get(String key, Class<T> type) {
+        return (T) get(key, TypeLiteral.of(type));
+    }
+
+    /**
+     * Accesses the current String value for the given key and tries to 
convert it
+     * using the {@link org.apache.tamaya.spi.PropertyConverter} instances 
provided by the current
+     * {@link org.apache.tamaya.spi.ConfigurationContext}.
+     *
+     * @param key  the property's absolute, or relative path, e.g. @code
+     *             a/b/c/d.myProperty}.
+     * @param type The target type required, not null.
+     * @param <T>  the value type
+     * @return the converted value, never null.
+     */
+    @Override
+    public <T> T get(String key, TypeLiteral<T> type) {
+        String value = get(key);
+        if (value != null) {
+            List<PropertyConverter<T>> converters = 
ConfigurationProvider.getConfigurationContext()
+                    .getPropertyConverters(type);
+            ConversionContext context = new ConversionContext.Builder(this,
+                    ConfigurationProvider.getConfigurationContext(), 
key,type).build();
+            for (PropertyConverter<T> converter : converters) {
+                try {
+                    T t = converter.convert(value, context);
+                    if (t != null) {
+                        return t;
+                    }
+                } catch (Exception e) {
+                    Logger.getLogger(getClass().getName())
+                            .log(Level.FINEST, "PropertyConverter: " + 
converter + " failed to convert value: " + value,
+                                    e);
+                }
+            }
+            throw new ConfigException("Unparseable config value for type: " + 
type.getRawType().getName() + ": " + key
+                    + ", supported formats: " + context.getSupportedFormats());
+        }
+
+        return null;
+    }
+
+    @Override
+    public <T> T getOrDefault(String key, TypeLiteral<T> type, T defaultValue) 
{
+        T val = get(key, type);
+        if(val==null){
+            return defaultValue;
+        }
+        return val;
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    @Override
+    public Configuration with(ConfigOperator operator) {
+        return operator.operate(this);
+    }
+
+    @Override
+    public <T> T query(ConfigQuery<T> query) {
+        return query.query(this);
+    }
+
+    @Override
+    public ConfigurationContext getContext() {
+        return ConfigurationFunctions.emptyConfigurationContext();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        FrozenConfiguration that = (FrozenConfiguration) o;
+        return properties.equals(that.properties);
+    }
+
+    @Override
+    public int hashCode() {
+        return properties.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "FrozenConfiguration{" +
+                "properties=" + properties +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java 
b/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java
new file mode 100644
index 0000000..81e6dca
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java
@@ -0,0 +1,126 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * PropertySource implementation that stores all current values of a given 
(possibly dynamic, contextual and non server
+ * capable instance) and is fully serializable. Note that hereby only the 
scannable key/value pairs are considered.
+ */
+public final class FrozenPropertySource implements PropertySource, 
Serializable {
+    private static final long serialVersionUID = -6373137316556444171L;
+    /**
+     * The ordinal.
+     */
+    private final int ordinal;
+    /**
+     * The properties read.
+     */
+    private Map<String, String> properties = new HashMap<>();
+    /**
+     * The PropertySource's name.
+     */
+    private final String name;
+
+    /**
+     * Constructor.
+     *
+     * @param propertySource The base PropertySource.
+     */
+    private FrozenPropertySource(PropertySource propertySource) {
+        this.properties.putAll(propertySource.getProperties());
+        this.properties.put("[meta]frozenAt", 
String.valueOf(System.currentTimeMillis()));
+        this.properties = Collections.unmodifiableMap(this.properties);
+        this.ordinal = propertySource.getOrdinal();
+        this.name = propertySource.getName();
+    }
+
+    /**
+     * Creates a new FrozenPropertySource instance based on a PropertySource 
given.
+     *
+     * @param propertySource the property source to be frozen, not null.
+     * @return the frozen property source.
+     */
+    public static FrozenPropertySource of(PropertySource propertySource) {
+        if (propertySource instanceof FrozenPropertySource) {
+            return (FrozenPropertySource) propertySource;
+        }
+        return new FrozenPropertySource(propertySource);
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public int getOrdinal() {
+        return this.ordinal;
+    }
+
+    @Override
+    public PropertyValue get(String key) {
+        return PropertyValue.of(key, this.properties.get(key), getName());
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    @Override
+    public boolean isScannable() {
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof FrozenPropertySource)) {
+            return false;
+        }
+        FrozenPropertySource that = (FrozenPropertySource) o;
+        return ordinal == that.ordinal && properties.equals(that.properties);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = ordinal;
+        result = 31 * result + properties.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "FrozenPropertySource{" +
+                "name=" + name +
+                ", ordinal=" + ordinal +
+                ", properties=" + properties +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/PropertySourceChange.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/PropertySourceChange.java 
b/events/src/main/java/org/apache/tamaya/events/PropertySourceChange.java
new file mode 100644
index 0000000..063612c
--- /dev/null
+++ b/events/src/main/java/org/apache/tamaya/events/PropertySourceChange.java
@@ -0,0 +1,242 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.spi.PropertySource;
+
+import java.beans.PropertyChangeEvent;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * 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 PropertySourceChangeBuilder}.
+ *
+ * Created by Anatole on 22.10.2014.
+ */
+public final class PropertySourceChange implements 
ConfigEvent<PropertySource>, Serializable{
+
+    private static final long serialVersionUID = 1L;
+    /** The base property provider/configuration. */
+    private final FrozenPropertySource propertySource;
+    /** The base version, usable for optimistic locking. */
+    private String version = UUID.randomUUID().toString();
+    /** The timestamp of the change set in millis from the epoch. */
+    private long timestamp = System.currentTimeMillis();
+    /** The recorded changes. */
+    private final Map<String,PropertyChangeEvent> changes = new HashMap<>();
+    /** The overall type of change. */
+    private final ChangeType changeType;
+
+    /**
+     * Constructor used by {@link PropertySourceChangeBuilder}.
+     * @param builder The builder used, not null.
+     */
+    PropertySourceChange(PropertySourceChangeBuilder builder) {
+        this.propertySource = FrozenPropertySource.of(builder.source);
+        for (PropertyChangeEvent c : builder.delta.values()) {
+            this.changes.put(c.getPropertyName(), c);
+        }
+        if(builder.version!=null){
+            this.version = builder.version;
+        }
+        if(builder.timestamp!=null){
+            this.timestamp = builder.timestamp;
+        }
+        this.changeType = builder.changeType;
+    }
+
+    /**
+     * Gets the type of change for this PropertySource.
+     * @return the type of change for this PropertySource, never null.
+     */
+    public ChangeType getChangeType(){
+        return this.changeType;
+    }
+
+    @Override
+    public Class<PropertySource> getResourceType() {
+        return PropertySource.class;
+    }
+
+    /**
+     * Get the underlying property provider/configuration.
+     * @return the underlying property provider/configuration, or null, if the 
change instance was deserialized.
+     */
+    @Override
+    public PropertySource getResource(){
+        return this.propertySource;
+    }
+
+    /**
+     * Get the base version, usable for optimistic locking.
+     * @return the base version.
+     */
+    @Override
+    public String getVersion(){
+        return version;
+    }
+
+    /**
+     * Get the timestamp in millis from the current epoch. it is expected that 
the timestamp and the version are unique to
+     * identify a changeset.
+     * @return the timestamp, when this changeset was created.
+     */
+    @Override
+    public long getTimestamp(){
+        return timestamp;
+    }
+
+    /**
+     * Get the changes recorded.
+     * @return the recorded changes, never null.
+     */
+    public Collection<PropertyChangeEvent> getChanges(){
+        return Collections.unmodifiableCollection(this.changes.values());
+    }
+
+    /**
+     * Access the number current removed entries.
+     * @return the number current removed entries.
+     */
+    public int getRemovedSize() {
+        int removedCount = 0;
+        for (PropertyChangeEvent ev : this.changes.values()) {
+            if (ev.getNewValue() == null) {
+                removedCount++;
+            }
+        }
+        return removedCount;
+//        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() {
+        int addedCount = 0;
+        for (PropertyChangeEvent ev : this.changes.values()) {
+            if (ev.getOldValue() == null &&
+                    ev.getNewValue() != null) {
+                addedCount++;
+            }
+        }
+        return addedCount;
+//        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() {
+        int updatedCount = 0;
+        for (PropertyChangeEvent ev : this.changes.values()) {
+            if (ev.getOldValue() != null && ev.getNewValue() != null) {
+                updatedCount++;
+            }
+        }
+        return updatedCount;
+//        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 isKeyAffected(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();
+    }
+
+
+    /**
+     * Create a change event for a new PropertySource that was added.
+     * @param propertySource the new property source, not null.
+     * @return a new PropertySourceChange, representing a PropertySource that 
was added.
+     */
+    public static PropertySourceChange ofAdded(PropertySource propertySource) {
+        return PropertySourceChangeBuilder.of(propertySource, 
ChangeType.NEW).build();
+    }
+
+    /**
+     * Create a change event for a deleted PropertySource.
+     * @param propertySource the deleted property source, not null.
+     * @return a new PropertySourceChange, representing a PropertySource that 
was deleted.
+     */
+    public static PropertySourceChange ofDeleted(PropertySource 
propertySource) {
+        return PropertySourceChangeBuilder.of(propertySource, 
ChangeType.DELETED).build();
+    }
+
+    @Override
+    public String toString() {
+        return "PropertySourceChange{" +
+                "changeType=" + changeType +
+                ", propertySource=" + propertySource +
+                ", version='" + version + '\'' +
+                ", timestamp=" + timestamp +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/89223dcd/events/src/main/java/org/apache/tamaya/events/PropertySourceChangeBuilder.java
----------------------------------------------------------------------
diff --git 
a/events/src/main/java/org/apache/tamaya/events/PropertySourceChangeBuilder.java
 
b/events/src/main/java/org/apache/tamaya/events/PropertySourceChangeBuilder.java
new file mode 100644
index 0000000..b7a4483
--- /dev/null
+++ 
b/events/src/main/java/org/apache/tamaya/events/PropertySourceChangeBuilder.java
@@ -0,0 +1,263 @@
+/*
+ * 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.events;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Models a set current changes applied to a {@link 
org.apache.tamaya.spi.PropertySource}. Consumers of these events
+ * can observing changes to property sources and
+ * <ol>
+ *     <li>Check if their current configuration instance ({@link 
org.apache.tamaya.spi.ConfigurationContext}
+ *     contains the changed {@link org.apache.tamaya.spi.PropertySource} 
(Note: the reference tova property source is never affected by a
+ *     change, its only the data of the property source).</li>
+ *     <li>If so corresponding action may be taken, such as reevaluating the 
configuration values (depending on
+ *     the update policy) or reevaluating the complete {@link 
org.apache.tamaya.Configuration} to create a change
+ *     event on configuration level.
+ * </ol>
+ */
+public final class PropertySourceChangeBuilder {
+    /**
+     * The recorded changes.
+     */
+    final SortedMap<String, PropertyChangeEvent> delta = new TreeMap<>();
+    /**
+     * The underlying configuration/provider.
+     */
+    final PropertySource source;
+    /**
+     * The version configured, or null, for generating a default.
+     */
+    String version;
+    /**
+     * The optional timestamp in millis of this epoch.
+     */
+    Long timestamp;
+
+    /** The type of change. */
+    ChangeType changeType;
+
+    /**
+     * Constructor.
+     *
+     * @param source the underlying configuration/provider, not null.
+     * @param changeType kind of change.
+     */
+    private PropertySourceChangeBuilder(PropertySource source, ChangeType 
changeType) {
+        this.source = Objects.requireNonNull(source);
+        this.changeType = Objects.requireNonNull(changeType);
+    }
+
+    /**
+     * Creates a new instance of this builder.
+     *
+     * @param source the underlying property provider/configuration, not null.
+     * @param changeType kind of change.
+     * @return the builder for chaining.
+     */
+    public static PropertySourceChangeBuilder of(PropertySource source, 
ChangeType changeType) {
+        return new PropertySourceChangeBuilder(source, changeType);
+    }
+
+    /**
+     * Compares the two property config/configurations and creates a 
collection current all changes
+     * that must be applied 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()) {
+            PropertyValue val = map2.get(en.getKey());
+            if (val == null) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), null, 
en.getValue()));
+            } else if (!val.getValue().equals(en.getValue())) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), 
val.getValue(), en.getValue()));
+            }
+        }
+        for (Map.Entry<String, String> en : map2.getProperties().entrySet()) {
+            PropertyValue val = map1.get(en.getKey());
+            if (val == null) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), 
en.getValue(), null));
+            } else if (!val.equals(en.getValue())) {
+                changes.add(new PropertyChangeEvent(map1, en.getKey(), 
en.getValue(), val.getValue()));
+            }
+        }
+        return changes;
+    }
+
+    /*
+     * Apply a version/UUID to the set being built.
+     * @param version the version to apply, or null, to let the system 
generate a version for you.
+     * @return the builder for chaining.
+     */
+    public PropertySourceChangeBuilder setVersion(String version) {
+        this.version = version;
+        return this;
+    }
+
+    /*
+     * Apply given timestamp to the set being built.
+     * @param version the version to apply, or null, to let the system 
generate a version for you.
+     * @return the builder for chaining.
+     */
+    public PropertySourceChangeBuilder setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+        return this;
+    }
+
+    /**
+     * 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 PropertySourceChangeBuilder addChanges(PropertySource newState) {
+        Collection<PropertyChangeEvent> events = 
PropertySourceChangeBuilder.compare(newState, this.source);
+        for (PropertyChangeEvent c : events) {
+            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 PropertySourceChangeBuilder remove(String key, String... otherKeys) 
{
+        PropertyValue oldValue = this.source.get(key);
+        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);
+            if (oldValue == null) {
+                this.delta.remove(addKey);
+            }
+            this.delta.put(addKey, new PropertyChangeEvent(this.source, 
addKey, oldValue, null));
+        }
+        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 PropertySourceChangeBuilder putAll(Map<String, String> changes) {
+        for (Map.Entry<String, String> en : 
this.source.getProperties().entrySet()) {
+            this.delta.put(en.getKey(), new PropertyChangeEvent(this.source, 
en.getKey(), null, en.getValue()));
+        }
+        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 PropertySourceChangeBuilder deleteAll() {
+        this.delta.clear();
+        for (Map.Entry<String, String> en : 
this.source.getProperties().entrySet()) {
+            this.delta.put(en.getKey(), new PropertyChangeEvent(this.source, 
en.getKey(), en.getValue(), 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();
+    }
+
+    public PropertySourceChangeBuilder setChangeType(ChangeType changeType) {
+        this.changeType = changeType;
+        return this;
+    }
+
+    /**
+     * Builds the corresponding change set.
+     *
+     * @return the new change set, never null.
+     */
+    public PropertySourceChange build() {
+        return new PropertySourceChange(this);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "PropertiesChangeBuilder [source=" + source + ", " +
+                ", delta=" + delta + "]";
+    }
+
+
+}


Reply via email to