Adapted to comply with JSR API. Signed-off-by: Anatole Tresch <[email protected]>
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/4869d946 Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/tree/4869d946 Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/diff/4869d946 Branch: refs/heads/configjsr Commit: 4869d946cb8a5730feb74c8dea379d9462427b5e Parents: 36b4466 Author: Anatole Tresch <[email protected]> Authored: Thu Dec 28 02:27:43 2017 +0100 Committer: Anatole Tresch <[email protected]> Committed: Thu Dec 28 02:27:43 2017 +0100 ---------------------------------------------------------------------- modules/events/pom.xml | 15 +- .../org/apache/tamaya/events/ConfigChange.java | 229 +++++++++++++++ .../tamaya/events/ConfigChangeBuilder.java | 274 ++++++++++++++++++ .../tamaya/events/ConfigEventManager.java | 9 +- .../tamaya/events/ConfigSourceChange.java | 211 ++++++++++++++ .../events/ConfigSourceChangeBuilder.java | 250 +++++++++++++++++ .../tamaya/events/ConfigurationChange.java | 230 --------------- .../events/ConfigurationChangeBuilder.java | 278 ------------------- .../org/apache/tamaya/events/FrozenConfig.java | 197 +++++++++++++ .../tamaya/events/FrozenConfigSource.java | 129 +++++++++ .../tamaya/events/FrozenConfiguration.java | 227 --------------- .../tamaya/events/FrozenPropertySource.java | 135 --------- .../tamaya/events/PropertySourceChange.java | 212 -------------- .../events/PropertySourceChangeBuilder.java | 252 ----------------- .../internal/DefaultConfigChangeObserver.java | 20 +- .../events/internal/LoggingConfigListener.java | 4 +- .../events/spi/ConfigEventManagerSpi.java | 3 +- .../events/ChangeableGlobalConfigSource.java | 62 +++++ .../events/ChangeableGlobalPropertySource.java | 62 ----- .../ChangeableThreadLocalPropertySource.java | 7 +- .../tamaya/events/ConfigChangeBuilderTest.java | 134 +++++++++ .../apache/tamaya/events/ConfigChangeTest.java | 161 +++++++++++ .../tamaya/events/ConfigSourceChangeTest.java | 180 ++++++++++++ .../events/ConfigurationChangeBuilderTest.java | 131 --------- .../tamaya/events/ConfigurationChangeTest.java | 162 ----------- .../tamaya/events/FrozenConfigSourceTest.java | 108 +++++++ .../tamaya/events/FrozenConfigurationTest.java | 23 +- .../tamaya/events/FrozenPropertySourceTest.java | 109 -------- .../tamaya/events/ObservedConfigTest.java | 4 +- .../tamaya/events/PropertySourceChangeTest.java | 180 ------------ .../tamaya/events/RandomConfigSource.java | 59 ++++ .../tamaya/events/RandomPropertySource.java | 65 ----- .../apache/tamaya/events/TestConfigView.java | 126 ++------- .../folderobserver/FileChangeListener.java | 6 +- .../ObservingPropertySourceProvider.java | 58 ++-- .../DefaultConfigChangeObserverTest.java | 6 +- .../org.apache.tamaya.spi.PropertySource | 2 +- 37 files changed, 2094 insertions(+), 2226 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/pom.xml ---------------------------------------------------------------------- diff --git a/modules/events/pom.xml b/modules/events/pom.xml index aaa650f..33904d0 100644 --- a/modules/events/pom.xml +++ b/modules/events/pom.xml @@ -34,25 +34,20 @@ under the License. <dependencies> <dependency> - <groupId>org.apache.tamaya</groupId> - <artifactId>tamaya-api</artifactId> - <version>${tamaya-apicore.version}</version> - </dependency> - <dependency> <groupId>org.apache.tamaya.ext</groupId> <artifactId>tamaya-functions</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.apache.tamaya</groupId> - <artifactId>tamaya-spisupport</artifactId> - <version>${project.version}</version> + <artifactId>tamaya-core</artifactId> + <version>${tamaya-apicore.version}</version> + <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tamaya</groupId> - <artifactId>tamaya-core</artifactId> - <version>${tamaya-apicore.version}</version> - <scope>runtime</scope> + <artifactId>tamaya-base</artifactId> + <version>${project.version}</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/ConfigChange.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/ConfigChange.java b/modules/events/src/main/java/org/apache/tamaya/events/ConfigChange.java new file mode 100644 index 0000000..01074b5 --- /dev/null +++ b/modules/events/src/main/java/org/apache/tamaya/events/ConfigChange.java @@ -0,0 +1,229 @@ +/* + * 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 javax.config.Config; +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 ConfigSourceChangeBuilder}. + * + * Created by Anatole on 22.10.2014. + */ +public final class ConfigChange implements ConfigEvent<Config>, Serializable{ + + private static final long serialVersionUID = 1L; + /** The base property provider/configuration. */ + private final FrozenConfig 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 ConfigChange emptyChangeSet(Config configuration){ + return ConfigChangeBuilder.of(configuration).build(); + } + + /** + * Constructor used by {@link ConfigSourceChangeBuilder}. + * @param builder The builder used, not null. + */ + ConfigChange(ConfigChangeBuilder builder) { + this.configuration = FrozenConfig.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<Config> getResourceType() { + return Config.class; + } + + /** + * Get the underlying property provider/configuration. + * @return the underlying property provider/configuration, never null. + */ + @Override + public Config 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.getPropertyName().startsWith("_")){ + continue; + } + 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.getPropertyName().startsWith("_")){ + continue; + } + 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.getPropertyName().startsWith("_")){ + continue; + } + 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{" + + "\n config-id = " + configuration.getOptionalValue("_id",String.class).orElse("-") + + "\n change-id = " + version + + "\n timestamp = " + timestamp + + "\n added = " + this.getAddedSize() + + "\n updated = " + this.getUpdatedSize() + + "\n removed = " + this.getRemovedSize() + '\n' + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/ConfigChangeBuilder.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/ConfigChangeBuilder.java b/modules/events/src/main/java/org/apache/tamaya/events/ConfigChangeBuilder.java new file mode 100644 index 0000000..a170ab4 --- /dev/null +++ b/modules/events/src/main/java/org/apache/tamaya/events/ConfigChangeBuilder.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 javax.config.Config; +import javax.config.ConfigProvider; +import java.beans.PropertyChangeEvent; +import java.util.*; + +/** + * Models a set 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, its only the data of the property source).</li> + * <li>If so corresponding actions might 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 ConfigChangeBuilder { + /** + * The recorded changes. + */ + final SortedMap<String, PropertyChangeEvent> delta = new TreeMap<>(); + /** + * The underlying configuration/provider. + */ + final Config 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 ConfigChangeBuilder(Config 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 ConfigChangeBuilder of() { + return new ConfigChangeBuilder(ConfigProvider.getConfig()); + } + + /** + * Creates a new instance current this builder. + * + * @param configuration the configuration changed, not null. + * @return the builder for chaining. + */ + public static ConfigChangeBuilder of(Config configuration) { + return new ConfigChangeBuilder(configuration); + } + + /** + * Compares the two property config/configurations and creates a collection with all changes + * that must be applied to render {@code previous} into {@code target}. + * + * @param previous the previous map, not null. + * @param current the target map, not null. + * @return a collection current change events, never {@code null}. + */ + public static Collection<PropertyChangeEvent> compare(Config previous, Config current) { + TreeMap<String, PropertyChangeEvent> events = new TreeMap<>(); + + for (String key : previous.getPropertyNames()) { + String previousValue = previous.getValue(key, String.class); + Optional<String> currentValue = current.getOptionalValue(key, String.class); + if(Objects.equals(currentValue.orElse(null), previousValue)){ + continue; + }else { + PropertyChangeEvent event = new PropertyChangeEvent(previous, key, previousValue, currentValue.orElse(null)); + events.put(key, event); + } + } + + for (String key : current.getPropertyNames()){ + Optional<String> previousValue = previous.getOptionalValue(key, String.class); + String currentValue = current.getOptionalValue(key, String.class).orElse(null); + if(Objects.equals(currentValue, previousValue.orElse(null))){ + continue; + }else{ + if (!previousValue.isPresent()) { + PropertyChangeEvent event = new PropertyChangeEvent(current, key, null, currentValue); + events.put(key, event); + } + // the other cases were already covered by the previous loop. + } + } + return events.values(); + } + + /* + * 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 ConfigChangeBuilder 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 ConfigChangeBuilder 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 ConfigChangeBuilder addChanges(Config newState) { + for (PropertyChangeEvent c : compare(this.source, newState)) { + 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 chaining. + */ + public ConfigChangeBuilder addChange(String key, String value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, + this.source.getOptionalValue(key, String.class).orElse(null), + 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 ConfigChangeBuilder removeKey(String key, String... otherKeys) { + Optional<String> oldValue = this.source.getOptionalValue(key, String.class); + if (!oldValue.isPresent()) { + this.delta.remove(key); + } + this.delta.put(key, new PropertyChangeEvent(this.source, key, oldValue, null)); + for (String addKey : otherKeys) { + oldValue = this.source.getOptionalValue(key, String.class); + if (!oldValue.isPresent()) { + 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 ConfigChangeBuilder 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 ConfigChangeBuilder removeAllKeys() { + this.delta.clear(); + for (String key : this.source.getPropertyNames()) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.getValue(key, String.class), + 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 ConfigChange build() { + return new ConfigChange(this); + } + + @Override + public String toString() { + return "ConfigurationChangeSetBuilder [config=" + source + ", " + + ", delta=" + delta + "]"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java b/modules/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java index 96dc9a5..29426a1 100644 --- a/modules/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java +++ b/modules/events/src/main/java/org/apache/tamaya/events/ConfigEventManager.java @@ -18,7 +18,6 @@ */ package org.apache.tamaya.events; -import org.apache.tamaya.ConfigException; import org.apache.tamaya.events.spi.ConfigEventManagerSpi; import org.apache.tamaya.spi.ServiceContextManager; @@ -37,7 +36,7 @@ public final class ConfigEventManager { ConfigEventManagerSpi spi = ServiceContextManager.getServiceContext() .getService(ConfigEventManagerSpi.class); if(spi==null){ - throw new ConfigException("No SPI registered for " + + throw new IllegalStateException("No SPI registered for " + ConfigEventManager.class.getName()); } return spi; @@ -110,7 +109,7 @@ public final class ConfigEventManager { } /** - * Publishes a {@link ConfigurationChange} synchronously to all interested listeners. + * Publishes a {@link ConfigChange} synchronously to all interested listeners. * * @param <T> the type of the event. * @param event the event, not null. @@ -120,7 +119,7 @@ public final class ConfigEventManager { } /** - * Publishes a {@link ConfigurationChange} asynchronously/multithreaded to all interested listeners. + * Publishes a {@link ConfigChange} asynchronously/multithreaded to all interested listeners. * * @param <T> the type of the event. * @param event the event, not null. @@ -134,7 +133,7 @@ public final class ConfigEventManager { * 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. + * to be registered, e.g. listening on {@link ConfigChange} events. * * @param enable whether to enable or disable the change monitoring. * http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChange.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChange.java b/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChange.java new file mode 100644 index 0000000..7bb5395 --- /dev/null +++ b/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChange.java @@ -0,0 +1,211 @@ +/* + * 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 javax.config.spi.ConfigSource; +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 ConfigSourceChangeBuilder}. + * + * Created by Anatole on 22.10.2014. + */ +public final class ConfigSourceChange implements ConfigEvent<ConfigSource>, Serializable{ + + private static final long serialVersionUID = 1L; + /** The base property provider/configuration. */ + private final FrozenConfigSource configSource; + /** 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<>(); + + /** + * Constructor used by {@link ConfigSourceChangeBuilder}. + * @param builder The builder used, not null. + */ + ConfigSourceChange(ConfigSourceChangeBuilder builder) { + this.configSource = FrozenConfigSource.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; + } + } + + @Override + public Class<ConfigSource> getResourceType() { + return ConfigSource.class; + } + + /** + * Get the underlying property provider/configuration. + * @return the underlying property provider/configuration, or null, if the change instance was deserialized. + */ + @Override + public ConfigSource getResource(){ + return this.configSource; + } + + /** + * 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(); + } + + + @Override + public String toString() { + return "ConfigSourceChange{" + + ", configSource=" + configSource + + ", version='" + version + '\'' + + ", timestamp=" + timestamp + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChangeBuilder.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChangeBuilder.java b/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChangeBuilder.java new file mode 100644 index 0000000..684bcf1 --- /dev/null +++ b/modules/events/src/main/java/org/apache/tamaya/events/ConfigSourceChangeBuilder.java @@ -0,0 +1,250 @@ +/* + * 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 javax.config.spi.ConfigSource; +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 ConfigSource}. Consumers of these events + * can observing changes to property sources and + * <ol> + * <li>Check if their current configuration instance ({@link javax.config.Config} + * contains the changed {@link ConfigSource} (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 javax.config.Config} to create a change + * event on configuration level. + * </ol> + */ +public final class ConfigSourceChangeBuilder { + /** + * The recorded changes. + */ + final SortedMap<String, PropertyChangeEvent> delta = new TreeMap<>(); + /** + * The underlying configuration/provider. + */ + final ConfigSource source; + /** + * The version configured, or null, for generating a default. + */ + String version; + /** + * The optional timestamp in millis of this epoch. + */ + Long timestamp; + + /** + * Constructor. + * + * @param source the underlying configuration/provider, not null. + */ + private ConfigSourceChangeBuilder(ConfigSource source) { + this.source = Objects.requireNonNull(source); + } + + /** + * Creates a new instance of this builder. + * + * @param source the underlying property provider/configuration, not null. + * @return the builder for chaining. + */ + public static ConfigSourceChangeBuilder of(ConfigSource source) { + return new ConfigSourceChangeBuilder(source); + } + + /** + * 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(ConfigSource map1, ConfigSource map2) { + List<PropertyChangeEvent> changes = new ArrayList<>(); + for (Map.Entry<String, String> en : map1.getProperties().entrySet()) { + String val = map2.getValue(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.getValue(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)); + } + } + 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 ConfigSourceChangeBuilder 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 ConfigSourceChangeBuilder 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 ConfigSourceChangeBuilder addChanges(ConfigSource newState) { + Collection<PropertyChangeEvent> events = ConfigSourceChangeBuilder.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 ConfigSourceChangeBuilder remove(String key, String... otherKeys) { + String oldValue = this.source.getValue(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.getValue(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 ConfigSourceChangeBuilder 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 ConfigSourceChangeBuilder 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(); + } + + + /** + * Builds the corresponding change set. + * + * @return the new change set, never null. + */ + public ConfigSourceChange build() { + return new ConfigSourceChange(this); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ConfigSourceChangeBuilder [source=" + source + ", " + + ", delta=" + delta + "]"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java b/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java deleted file mode 100644 index 9cdd4fc..0000000 --- a/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChange.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.tamaya.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.getPropertyName().startsWith("_")){ - continue; - } - 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.getPropertyName().startsWith("_")){ - continue; - } - 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.getPropertyName().startsWith("_")){ - continue; - } - 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{" + - "\n configuration-id = " + configuration.getOrDefault("_id", "-") + - "\n change-id = " + version + - "\n timestamp = " + timestamp + - "\n added = " + this.getAddedSize() + - "\n updated = " + this.getUpdatedSize() + - "\n removed = " + this.getRemovedSize() + '\n' + - '}'; - } -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java b/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java deleted file mode 100644 index 8becb11..0000000 --- a/modules/events/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.tamaya.events; - -import org.apache.tamaya.Configuration; -import org.apache.tamaya.ConfigurationProvider; - -import java.beans.PropertyChangeEvent; -import java.util.Collection; -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 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, its only the data of the property source).</li> - * <li>If so corresponding actions might 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 with all changes - * that must be applied to render {@code previous} into {@code target}. - * - * @param previous the previous map, not null. - * @param current the target map, not null. - * @return a collection current change events, never {@code null}. - */ - public static Collection<PropertyChangeEvent> compare(Configuration previous, Configuration current) { - TreeMap<String, PropertyChangeEvent> events = new TreeMap<>(); - - for (Map.Entry<String, String> en : previous.getProperties().entrySet()) { - String key = en.getKey(); - String previousValue = en.getValue(); - String currentValue = current.get(en.getKey()); - if(Objects.equals(currentValue, previousValue)){ - continue; - }else { - PropertyChangeEvent event = new PropertyChangeEvent(previous, key, previousValue, currentValue); - events.put(key, event); - } - } - - for (Map.Entry<String, String> en : current.getProperties().entrySet()) { - String key = en.getKey(); - String previousValue = previous.get(en.getKey()); - String currentValue = en.getValue(); - if(Objects.equals(currentValue, previousValue)){ - continue; - }else{ - if (previousValue == null) { - PropertyChangeEvent event = new PropertyChangeEvent(current, key, null, currentValue); - events.put(key, event); - } - // the other cases were already covered by the previous loop. - } - } - return events.values(); - } - - /* - * 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(this.source, newState)) { - 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 chaining. - */ - 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); - } - - @Override - public String toString() { - return "ConfigurationChangeSetBuilder [config=" + source + ", " + - ", delta=" + delta + "]"; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfig.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfig.java b/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfig.java new file mode 100644 index 0000000..dca2204 --- /dev/null +++ b/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfig.java @@ -0,0 +1,197 @@ +/* + * 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.base.convert.ConversionContext; +import org.apache.tamaya.base.convert.ConverterManager; +import org.apache.tamaya.spi.ConfigContext; +import org.apache.tamaya.spi.ConfigContextSupplier; + +import javax.config.Config; +import javax.config.spi.ConfigSource; +import javax.config.spi.Converter; +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.*; +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 FrozenConfig implements Config, Serializable { + private static final long serialVersionUID = -6373137316556444171L; + + /** + * The properties frozen. + */ + private Map<String, String> properties = new HashMap<>(); + private long frozenAt = System.nanoTime(); + private UUID id = UUID.randomUUID(); + private ConverterManager converterManager = new ConverterManager(); + + /** + * Constructor. + * + * @param config The base configuration. + */ + private FrozenConfig(Config config) { + for(String key:config.getPropertyNames()) { + this.properties.put(key, config.getValue(key, String.class)); + } + this.properties = Collections.unmodifiableMap(this.properties); + if(config instanceof ConfigContextSupplier){ + ConfigContext ctx = ((ConfigContextSupplier)config).getConfigContext(); + for(Map.Entry<Type, List<Converter>> en:ctx.getConverters().entrySet()) { + this.converterManager.addConverter(en.getKey(), en.getValue()); + } + } + } + + /** + * 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 FrozenConfig of(Config config) { + if (config instanceof FrozenConfig) { + return (FrozenConfig) config; + } + return new FrozenConfig(config); + } + + public String getValue(String key) { + String val = this.properties.get(key); + if(val==null){ + throw new NoSuchElementException("No such config found: " + key); + } + return val; + } + + @Override + public <T> Optional<T> getOptionalValue(String key, Class<T> type) { + String value = this.properties.get(key); + if (value != null) { + List<Converter> converters = converterManager.getConverters(type); + ConversionContext context = new ConversionContext.Builder(this, key,type).build(); + ConversionContext.setContext(context); + try { + for (Converter<T> converter : converters) { + try { + T t = converter.convert(value); + if (t != null) { + return Optional.of((T) t); + } + } catch (Exception e) { + Logger.getLogger(getClass().getName()) + .log(Level.FINEST, "PropertyConverter: " + converter + " failed to convert value: " + value, + e); + } + } + }finally{ + ConversionContext.reset(); + } + if(String.class==type){ + return Optional.of((T)value); + } + throw new IllegalArgumentException("Unparseable config value for type: " + type.getName() + ": " + key + + ", supported formats: " + context.getSupportedFormats()); + } + return Optional.empty(); + } + + + @SuppressWarnings("unchecked") + @Override + public <T> T getValue(String key, Class<T> type) { + return getOptionalValue(key, type) + .orElseThrow(() -> new NoSuchElementException("No such config found: " + key)); + } + + @Override + public Iterable<String> getPropertyNames() { + return properties.keySet(); + } + + @Override + public List<ConfigSource> getConfigSources() { + return Collections.emptyList(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + FrozenConfig that = (FrozenConfig) o; + + if (frozenAt != that.frozenAt) { + return false; + } + if (properties != null ? !properties.equals(that.properties) : that.properties != null) { + return false; + } + return id != null ? id.equals(that.id) : that.id == null; + } + + @Override + public int hashCode() { + int result = properties != null ? properties.hashCode() : 0; + result = 31 * result + (int) (frozenAt ^ (frozenAt >>> 32)); + result = 31 * result + (id != null ? id.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "FrozenConfiguration{" + + "id=" + getId() + "," + + "frozenAt=" + getFrozenAt() + "," + + "properties=" + properties + + '}'; + } + + /** + * <p>Returns the moment in time when this frozen configuration has been created.</p> + * + * <p>The time is taken from {@linkplain System#currentTimeMillis()}</p> + * + * @see System#currentTimeMillis() + * @return the moment in time when this configuration has been created + */ + public long getFrozenAt() { + return frozenAt; + } + + /** + * <p>Returns the unique id of this frozen configuration.</p> + * + * @return the unique id of this frozen configuration, never {@code null} + */ + public UUID getId() { + return id; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfigSource.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfigSource.java b/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfigSource.java new file mode 100644 index 0000000..4bdd2a7 --- /dev/null +++ b/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfigSource.java @@ -0,0 +1,129 @@ +/* + * 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.base.configsource.ConfigSourceComparator; + +import javax.config.spi.ConfigSource; +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 FrozenConfigSource implements ConfigSource, 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; + + private long frozenAt = System.currentTimeMillis(); + + /** + * Constructor. + * + * @param configSource The base ConfigSource. + */ + private FrozenConfigSource(ConfigSource configSource) { + this.properties.putAll(configSource.getProperties()); + this.properties = Collections.unmodifiableMap(this.properties); + this.ordinal = ConfigSourceComparator.getOrdinal(configSource); + this.name = configSource.getName(); + } + + /** + * Creates a new FrozenPropertySource instance based on a PropertySource given. + * + * @param configSource the config source to be frozen, not null. + * @return the frozen property source. + */ + public static FrozenConfigSource of(ConfigSource configSource) { + if (configSource instanceof FrozenConfigSource) { + return (FrozenConfigSource) configSource; + } + return new FrozenConfigSource(configSource); + } + + @Override + public String getName() { + return this.name; + } + + public int getOrdinal() { + return this.ordinal; + } + + /** + * Get the creation timestamp of this instance. + * @return the creation timestamp + */ + public long getFrozenAt(){ + return frozenAt; + } + + @Override + public String getValue(String key) { + return this.properties.get(key); + } + + @Override + public Map<String,String> getProperties() { + return Collections.unmodifiableMap(properties); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof FrozenConfigSource)) { + return false; + } + FrozenConfigSource that = (FrozenConfigSource) 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/4869d946/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java b/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java deleted file mode 100644 index 21ef873..0000000 --- a/modules/events/src/main/java/org/apache/tamaya/events/FrozenConfiguration.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.tamaya.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.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.*; -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<>(); - private long frozenAt = System.nanoTime(); - private UUID id = UUID.randomUUID(); - - /** - * Constructor. - * - * @param config The base configuration. - */ - private FrozenConfiguration(Configuration config) { - this.properties.putAll(config.getProperties()); - 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; - } - - @SuppressWarnings("unchecked") - @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 = getContext() - .getPropertyConverters(type); - ConversionContext context = new ConversionContext.Builder(this, - getContext(), 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; - - if (frozenAt != that.frozenAt) { - return false; - } - if (properties != null ? !properties.equals(that.properties) : that.properties != null) { - return false; - } - return id != null ? id.equals(that.id) : that.id == null; - } - - @Override - public int hashCode() { - int result = properties != null ? properties.hashCode() : 0; - result = 31 * result + (int) (frozenAt ^ (frozenAt >>> 32)); - result = 31 * result + (id != null ? id.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "FrozenConfiguration{" + - "id=" + getId() + "," + - "frozenAt=" + getFrozenAt() + "," + - "properties=" + properties + - '}'; - } - - /** - * <p>Returns the moment in time when this frozen configuration has been created.</p> - * - * <p>The time is taken from {@linkplain System#currentTimeMillis()}</p> - * - * @see System#currentTimeMillis() - * @return the moment in time when this configuration has been created - */ - public long getFrozenAt() { - return frozenAt; - } - - /** - * <p>Returns the unique id of this frozen configuration.</p> - * - * @return the unique id of this frozen configuration, never {@code null} - */ - public UUID getId() { - return id; - } -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/4869d946/modules/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java ---------------------------------------------------------------------- diff --git a/modules/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java b/modules/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java deleted file mode 100644 index 0bc71ce..0000000 --- a/modules/events/src/main/java/org/apache/tamaya/events/FrozenPropertySource.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.tamaya.events; - -import org.apache.tamaya.spi.PropertySource; -import org.apache.tamaya.spi.PropertyValue; -import org.apache.tamaya.spisupport.PropertySourceComparator; - -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, PropertyValue> properties = new HashMap<>(); - /** - * The PropertySource's name. - */ - private final String name; - - private long frozenAt = System.currentTimeMillis(); - - /** - * Constructor. - * - * @param propertySource The base PropertySource. - */ - private FrozenPropertySource(PropertySource propertySource) { - this.properties.putAll(propertySource.getProperties()); - this.properties = Collections.unmodifiableMap(this.properties); - this.ordinal = PropertySourceComparator.getOrdinal(propertySource); - 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; - } - - public int getOrdinal() { - return this.ordinal; - } - - /** - * Get the creation timestamp of this instance. - * @return the creation timestamp - */ - public long getFrozenAt(){ - return frozenAt; - } - - @Override - public PropertyValue get(String key) { - return this.properties.get(key); - } - - @Override - public Map<String, PropertyValue> 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 + - '}'; - } -}
