You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by pl...@apache.org on 2016/10/17 18:21:56 UTC
[36/50] [abbrv] incubator-tamaya-extensions git commit: TAMAYA-175
Moved the events module to the directory events after extracting it for the
Tamaya main repository.
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 <pl...@apache.org>
Authored: Tue Sep 27 00:17:11 2016 +0200
Committer: Oliver B. Fischer <pl...@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 + "]";
+ }
+
+
+}