http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/delta/ConfigurationChangeBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/delta/ConfigurationChangeBuilder.java b/src/main/java/org/apache/tamaya/events/delta/ConfigurationChangeBuilder.java deleted file mode 100644 index ff5e26b..0000000 --- a/src/main/java/org/apache/tamaya/events/delta/ConfigurationChangeBuilder.java +++ /dev/null @@ -1,262 +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.delta; - -import org.apache.tamaya.Configuration; - -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. - */ - 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. - * - * @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) { - changes.putAll(changes); - return this; - } - - /** - * This method will create a change set that clears all entries fromMap the given base configuration/properties. - * - * @return the builder for chaining. - */ - public 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/384b09eb/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChange.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChange.java b/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChange.java deleted file mode 100644 index 0aef2fd..0000000 --- a/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChange.java +++ /dev/null @@ -1,192 +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.delta; - -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 current changes that were applied or could be applied. - * This class is immutable and thread-safe. To create instances use - * {@link org.apache.tamaya.events.delta.PropertySourceChangeBuilder}. - * - * Created by Anatole on 22.10.2014. - */ -public final class ConfigurationContextChange implements Serializable{ - - private static final long serialVersionUID = 1L; - /** The base property provider/configuration. */ - private 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(); - - /** - * Get an empty change set for the given provider. - * @return an empty ConfigurationChangeSet instance. - */ - public static ConfigurationContextChange emptyChangeSet(){ - return ConfigurationContextChangeBuilder.of().build(); - } - - /** - * Constructor used by {@link org.apache.tamaya.events.delta.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; - } - } - - /** - * Get the base version, usable for optimistic locking. - * @return the base version. - */ - 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. - */ - 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/384b09eb/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChangeBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChangeBuilder.java b/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChangeBuilder.java deleted file mode 100644 index 3e21635..0000000 --- a/src/main/java/org/apache/tamaya/events/delta/ConfigurationContextChangeBuilder.java +++ /dev/null @@ -1,159 +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.delta; - -import org.apache.tamaya.spi.PropertySource; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * 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 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; - - /** - * Constructor. - */ - private ConfigurationContextChangeBuilder() { - } - - /** - * Creates a new instance current this builder. - * - * @return the builder for chaining. - */ - public static ConfigurationContextChangeBuilder of() { - return new ConfigurationContextChangeBuilder(); - } - - /* - * 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 version the version to apply, or null, to let the system generate a version for you. - * @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/384b09eb/src/main/java/org/apache/tamaya/events/delta/PropertySourceChange.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/delta/PropertySourceChange.java b/src/main/java/org/apache/tamaya/events/delta/PropertySourceChange.java deleted file mode 100644 index 330f3b0..0000000 --- a/src/main/java/org/apache/tamaya/events/delta/PropertySourceChange.java +++ /dev/null @@ -1,236 +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.delta; - -import org.apache.tamaya.events.ChangeNotification; -import org.apache.tamaya.events.FrozenPropertySource; -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 org.apache.tamaya.events.delta.PropertySourceChangeBuilder}. - * - * Created by Anatole on 22.10.2014. - */ -public final class PropertySourceChange implements ChangeNotification<PropertySource>, Serializable{ - - private static final long serialVersionUID = 1L; - /** The base property provider/configuration. */ - private 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 Map<String,PropertyChangeEvent> changes = new HashMap<>(); - /** The overall type of change. */ - private ChangeType changeType; - - /** - * Constructor used by {@link org.apache.tamaya.events.delta.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; - } - - /** - * Get the underlying property provider/configuration. - * @return the underlying property provider/configuration, or null, if the change instance was deserialized. - */ - public PropertySource getResource(){ - return this.propertySource; - } - - /** - * Get the base version, usable for optimistic locking. - * @return the base version. - */ - 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. - */ - 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/384b09eb/src/main/java/org/apache/tamaya/events/delta/PropertySourceChangeBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/delta/PropertySourceChangeBuilder.java b/src/main/java/org/apache/tamaya/events/delta/PropertySourceChangeBuilder.java deleted file mode 100644 index a0c0027..0000000 --- a/src/main/java/org/apache/tamaya/events/delta/PropertySourceChangeBuilder.java +++ /dev/null @@ -1,258 +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.delta; - -import org.apache.tamaya.spi.PropertySource; - -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. - */ - 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. - */ - private PropertySourceChangeBuilder(PropertySource source, ChangeType changeType) { - this.source = Objects.requireNonNull(source); - this.changeType = Objects.requireNonNull(changeType); - } - - /** - * Creates a new instance current this builder. - * - * @param source the underlying property provider/configuration, not null. - * @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 appied to render {@code map1} into {@code map2}. - * - * @param map1 the source map, not null. - * @param map2 the target map, not null. - * @return a collection current change events, never null. - */ - public static Collection<PropertyChangeEvent> compare(PropertySource map1, PropertySource map2) { - List<PropertyChangeEvent> changes = new ArrayList<>(); - for (Map.Entry<String, String> en : map1.getProperties().entrySet()) { - 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(), 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 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) { - 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 PropertySourceChangeBuilder putAll(Map<String, String> changes) { - changes.putAll(changes); - return this; - } - - /** - * This method will create a change set that clears all entries fromMap the given base configuration/properties. - * - * @return the builder for chaining. - */ - public 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 + "]"; - } - - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/folderobserver/FileChangeListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/folderobserver/FileChangeListener.java b/src/main/java/org/apache/tamaya/events/folderobserver/FileChangeListener.java index dd64145..af51063 100644 --- a/src/main/java/org/apache/tamaya/events/folderobserver/FileChangeListener.java +++ b/src/main/java/org/apache/tamaya/events/folderobserver/FileChangeListener.java @@ -19,6 +19,7 @@ package org.apache.tamaya.events.folderobserver; import org.apache.tamaya.ConfigException; +import org.apache.tamaya.events.PropertySourceChange; import java.io.IOException; import java.nio.file.FileSystem; @@ -34,7 +35,7 @@ import java.util.logging.Logger; /** * Class that has the responsibility to watch the folder and then publish the changes to a - * {@link org.apache.tamaya.events.delta.PropertySourceChange}. + * {@link PropertySourceChange}. * @see ObservingPropertySourceProvider * This listener will wait to events and wait to one second to watch again. * <p>If new file was created or modified will commit from this file.</p> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/folderobserver/ObservingPropertySourceProvider.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/folderobserver/ObservingPropertySourceProvider.java b/src/main/java/org/apache/tamaya/events/folderobserver/ObservingPropertySourceProvider.java index 9b88c0a..14692f4 100644 --- a/src/main/java/org/apache/tamaya/events/folderobserver/ObservingPropertySourceProvider.java +++ b/src/main/java/org/apache/tamaya/events/folderobserver/ObservingPropertySourceProvider.java @@ -19,9 +19,11 @@ package org.apache.tamaya.events.folderobserver; import org.apache.tamaya.ConfigException; +import org.apache.tamaya.ConfigurationProvider; import org.apache.tamaya.events.ConfigEventManager; -import org.apache.tamaya.events.delta.ConfigurationContextChange; -import org.apache.tamaya.events.delta.ConfigurationContextChangeBuilder; +import org.apache.tamaya.events.PropertySourceChange; +import org.apache.tamaya.events.ConfigurationContextChange; +import org.apache.tamaya.events.ConfigurationContextChangeBuilder; import org.apache.tamaya.format.ConfigurationData; import org.apache.tamaya.format.ConfigurationFormat; import org.apache.tamaya.format.FlattenedDefaultPropertySource; @@ -50,7 +52,7 @@ import java.util.logging.Logger; * This implementation run in a folder taking up all file compatible with the given * ConfigurationFormats. When a file is added, deleted or modified the PropertySourceProvider * will adapt the changes automatically and trigger according - * {@link org.apache.tamaya.events.delta.PropertySourceChange} events. + * {@link PropertySourceChange} events. * The default folder is META-INF/config, but you can change using the absolute path in * "-Dtamaya.configdir" parameter. */ http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/internal/DefaultConfigChangeObserver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/internal/DefaultConfigChangeObserver.java b/src/main/java/org/apache/tamaya/events/internal/DefaultConfigChangeObserver.java new file mode 100644 index 0000000..8903566 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/internal/DefaultConfigChangeObserver.java @@ -0,0 +1,107 @@ +/* + * 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.internal; + +import org.apache.tamaya.ConfigurationProvider; +import org.apache.tamaya.events.*; + +import java.util.*; +import java.util.logging.Logger; + +/** + * Timer task that regularly checks the configuration for changes. + */ +public class DefaultConfigChangeObserver { + + private static final long START_DELAY = 5000L; + + private static final Logger LOG = Logger.getLogger(DefaultConfigChangeObserver.class.getName()); + + private Timer timer = new Timer("DefaultConfigChangeObserver", true); + + private long checkPeriod = 2000L; + + private volatile FrozenConfiguration lastConfig; + + private volatile boolean running; + + /** + * Constructor. Also loads all registered listeners. + */ + public DefaultConfigChangeObserver() { + LOG.info("Registering config change observer, rechecking config changes every " + checkPeriod + " ms."); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + if(running) { + checkConfigurationUpdate(); + } + } + }, START_DELAY, checkPeriod); + } + + + public void checkConfigurationUpdate() { + LOG.finest("Checking configuration for changes..."); + FrozenConfiguration newConfig = FrozenConfiguration.of(ConfigurationProvider.getConfiguration()); + ConfigurationChange changes = null; + if(lastConfig==null){ + changes = ConfigurationChangeBuilder.of(newConfig).putAll(newConfig.getProperties()) + .build(); + }else{ + changes = ConfigurationChangeBuilder.of(lastConfig).addChanges(newConfig) + .build(); + } + if(!changes.isEmpty()) { + LOG.info("Identified configuration changes, publishing change event..."); + ConfigEventManager.fireEvent(changes); + } + } + + public long getCheckPeriod() { + return checkPeriod; + } + + public boolean isMonitoring(){ + return running; + } + + public void enableMonitoring(boolean enable){ + this.running = true; + } + + /** + * Sets the new check period, cancels the currently running timer and schedules a new task with the new checkperiod + * and a startup delay of 500ms. + * @param checkPeriod + */ + public void setCheckPeriod(long checkPeriod) { + LOG.finest("Resetting check period to " + checkPeriod + " ms, reregistering timer."); + this.checkPeriod = checkPeriod; + timer.cancel(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + if(running) { + checkConfigurationUpdate(); + } + } + }, 500L, checkPeriod); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/internal/DefaultConfigEventManagerSpi.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/internal/DefaultConfigEventManagerSpi.java b/src/main/java/org/apache/tamaya/events/internal/DefaultConfigEventManagerSpi.java index 6773c7d..75f2c91 100644 --- a/src/main/java/org/apache/tamaya/events/internal/DefaultConfigEventManagerSpi.java +++ b/src/main/java/org/apache/tamaya/events/internal/DefaultConfigEventManagerSpi.java @@ -18,18 +18,15 @@ */ package org.apache.tamaya.events.internal; -import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.events.ConfigEvent; import org.apache.tamaya.events.ConfigEventListener; import org.apache.tamaya.events.spi.ConfigEventManagerSpi; import org.apache.tamaya.spi.ServiceContextManager; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; @@ -41,14 +38,18 @@ public class DefaultConfigEventManagerSpi implements ConfigEventManagerSpi { private static final Logger LOG = Logger.getLogger(DefaultConfigEventManagerSpi.class.getName()); - private Map<Type, List<ConfigEventListener<?>>> listenerMap = new ConcurrentHashMap<>(); + private Map<Class,List<ConfigEventListener>> listeners = new ConcurrentHashMap<>(); + + private ExecutorService publisher = Executors.newCachedThreadPool(); + + private DefaultConfigChangeObserver changeObserver = new DefaultConfigChangeObserver(); /** * Constructor. Also loads all registered listeners. */ public DefaultConfigEventManagerSpi() { try { - for (ConfigEventListener<?> l : ServiceContextManager.getServiceContext().getServices(ConfigEventListener.class)) { + for (ConfigEventListener l : ServiceContextManager.getServiceContext().getServices(ConfigEventListener.class)) { try { addListener(l); } catch (Exception e) { @@ -61,37 +62,76 @@ public class DefaultConfigEventManagerSpi implements ConfigEventManagerSpi { } @Override - public <T> void addListener(ConfigEventListener<T> l) { - Type type = TypeLiteral.getGenericInterfaceTypeParameters(l.getClass(), ConfigEventListener.class)[0]; - List<ConfigEventListener<?>> listeners = listenerMap.get(type); - if (listeners == null) { - listeners = Collections.synchronizedList(new ArrayList<ConfigEventListener<?>>()); - listenerMap.put(type, listeners); + public void addListener(ConfigEventListener l){ + addListener(l, ConfigEvent.class); + } + + @Override + public <T extends ConfigEvent> void addListener(ConfigEventListener l, Class<T> eventType){ + List<ConfigEventListener> ls = listeners.get(eventType); + if(ls==null){ + ls = Collections.synchronizedList(new ArrayList<ConfigEventListener>()); + listeners.put(eventType, ls); } - synchronized (listeners) { - if (!listeners.contains(l)) { - listeners.add(l); + synchronized (ls){ + if(!ls.contains(l)){ + ls.add(l); } } } @Override - public <T> void removeListener(ConfigEventListener<T> l) { - Type type = TypeLiteral.getGenericInterfaceTypeParameters(l.getClass(), ConfigEventListener.class)[0]; - List<ConfigEventListener<?>> listeners = listenerMap.get(type); - if (listeners != null) { - synchronized (listeners) { - listeners.remove(l); + public void removeListener(ConfigEventListener l){ + removeListener(l, ConfigEvent.class); + } + + @Override + public <T extends ConfigEvent> void removeListener(ConfigEventListener l, Class<T> eventType) { + List<ConfigEventListener> targets = this.listeners.get(eventType); + if(targets!=null) { + // forward to explicit listeners + synchronized (targets) { + targets.remove(l); } } } @Override - public <T> void fireEvent(T event, Class<T> eventType) { - List<ConfigEventListener<?>> listeners = listenerMap.get(eventType); - if (listeners != null) { - synchronized (listeners) { - for (ConfigEventListener l : listeners) { + public Collection<? extends ConfigEventListener> getListeners(Class<? extends ConfigEvent> eventType) { + List<ConfigEventListener> targets = this.listeners.get(eventType); + if(targets!=null){ + synchronized(targets){ + return new ArrayList<>(targets); + } + } + return Collections.emptyList(); + } + + @Override + public Collection<? extends ConfigEventListener> getListeners() { + Set<ConfigEventListener> targets = new HashSet<>(); + for(List<ConfigEventListener> l:this.listeners.values()){ + targets.addAll(l); + } + return targets; + } + + @Override + public void fireEvent(ConfigEvent<?> event) { + List<ConfigEventListener> targets = this.listeners.get(event.getClass()); + if(targets!=null) { + // forward to explicit listeners + synchronized (targets) { + for (ConfigEventListener l : targets) { + l.onConfigEvent(event); + } + } + } + // forward to global listeners + targets = this.listeners.get(ConfigEvent.class); + if(targets!=null) { + synchronized (targets) { + for (ConfigEventListener l : targets) { l.onConfigEvent(event); } } @@ -99,12 +139,64 @@ public class DefaultConfigEventManagerSpi implements ConfigEventManagerSpi { } @Override - public <T> Collection<ConfigEventListener<T>> getListeners(Class<T> eventType) { - List<ConfigEventListener<?>> listeners = - listenerMap.get(eventType); - if (listeners != null) { - return Collection.class.cast(listeners); + public void fireEventAsynch(ConfigEvent<?> event) { + List<ConfigEventListener> targets = this.listeners.get(event.getClass()); + if(targets!=null) { + // forward to explicit listeners + synchronized (targets) { + for (ConfigEventListener l : targets) { + publisher.execute(new PublishConfigChangeTask(l, event)); + } + } + } + // forward to global listeners + targets = this.listeners.get(ConfigEvent.class); + if(targets!=null) { + synchronized (targets) { + for (ConfigEventListener l : targets) { + publisher.execute(new PublishConfigChangeTask(l, event)); + } + } + } + } + + @Override + public long getChangeMonitoringPeriod() { + return changeObserver.getCheckPeriod(); + } + + @Override + public void setChangeMonitoringPeriod(long millis){ + changeObserver.setCheckPeriod(millis); + } + + @Override + public boolean isChangeMonitorActive() { + return changeObserver.isMonitoring(); + } + + @Override + public void enableChangeMonitor(boolean enable) { + changeObserver.enableMonitoring(enable); + } + + + /** + * Tasks to inform observers on detected configuration changes. + */ + private static final class PublishConfigChangeTask implements Runnable{ + + private ConfigEventListener l; + private ConfigEvent<?> changes; + + public PublishConfigChangeTask(ConfigEventListener l, ConfigEvent<?> changes) { + this.l = Objects.requireNonNull(l); + this.changes = Objects.requireNonNull(changes); + } + + @Override + public void run() { + l.onConfigEvent(changes); } - return Collections.emptyList(); } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/internal/DefaultConfigObserverSpi.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/internal/DefaultConfigObserverSpi.java b/src/main/java/org/apache/tamaya/events/internal/DefaultConfigObserverSpi.java deleted file mode 100644 index 6bcb44c..0000000 --- a/src/main/java/org/apache/tamaya/events/internal/DefaultConfigObserverSpi.java +++ /dev/null @@ -1,158 +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.internal; - -import org.apache.tamaya.ConfigurationProvider; -import org.apache.tamaya.events.ConfigEventManager; -import org.apache.tamaya.events.ConfigListener; -import org.apache.tamaya.events.FrozenConfiguration; -import org.apache.tamaya.events.delta.ConfigurationChange; -import org.apache.tamaya.events.delta.ConfigurationChangeBuilder; -import org.apache.tamaya.events.spi.ConfigObserverSpi; -import org.apache.tamaya.spi.ServiceContextManager; - -import java.beans.PropertyChangeEvent; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Default implementation of {@link org.apache.tamaya.events.spi.ConfigObserverSpi} just forwarding all - * events synchronously to the listeners. - */ -public class DefaultConfigObserverSpi implements ConfigObserverSpi { - - private static final long START_DELAY = 5000L; - - private static final Logger LOG = Logger.getLogger(DefaultConfigObserverSpi.class.getName()); - - private Set<String> keys = new HashSet<>(); - - private Timer timer = new Timer("ConfigurationObserver", true); - - private long checkPeriod = 2000L; - - private volatile FrozenConfiguration lastConfig; - - private ExecutorService publisher = Executors.newCachedThreadPool(); - - private volatile boolean running; - - /** - * Constructor. Also loads all registered listeners. - */ - public DefaultConfigObserverSpi() { - try { - // Load and register ConfigListener from the current ServiceContext - for (ConfigListener l : ServiceContextManager.getServiceContext().getServices(ConfigListener.class)) { - ConfigEventManager.addListener(l); - } - } catch (Exception e) { - LOG.log(Level.WARNING, "Failed to load configured listeners.", e); - } - timer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - if(running) { - checkConfigurationUpdate(); - } - } - }, START_DELAY, checkPeriod); - } - - public void checkConfigurationUpdate() { - LOG.finest("Checking configuration for changes..."); - FrozenConfiguration newConfig = FrozenConfiguration.of(ConfigurationProvider.getConfiguration()); - ConfigurationChange changes = null; - if(lastConfig==null){ - changes = ConfigurationChangeBuilder.of(newConfig).putAll(newConfig.getProperties()) - .build(); - }else{ - changes = ConfigurationChangeBuilder.of(lastConfig).addChanges(newConfig) - .build(); - } - Set<ConfigListener> affected = new HashSet<>(); - for(PropertyChangeEvent evt: changes.getChanges()) { - for (String key : keys) { - if (evt.getPropertyName().matches(key)) { - ConfigEventManager.fireEvent(changes); - return; - } - } - } - } - - @Override - public synchronized <T> void addObservedKeys(Collection<String> keys) { - this.keys.addAll(keys); - } - - @Override - public synchronized <T> void removeObservedKeys(Collection<String> keys) { - this.keys.removeAll(keys); - } - - - @Override - public synchronized Set<String> getObservedKeys() { - return Collections.unmodifiableSet(this.keys); - } - - @Override - public long getCheckPeriod() { - return checkPeriod; - } - - @Override - public boolean isRunning(){ - return running; - } - - @Override - public void enableObservation(boolean enable){ - this.running = true; - } - - /** - * Tasks to inform observers on detected configuration changes. - */ - private static final class PublishConfigChangeTask implements Runnable{ - - private ConfigListener l; - private ConfigurationChange changes; - - public PublishConfigChangeTask(ConfigListener l, ConfigurationChange changes) { - this.l = Objects.requireNonNull(l); - this.changes = Objects.requireNonNull(changes); - } - - @Override - public void run() { - l.onConfigEvent(changes); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/internal/DefaultConfigurationContextChangeListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/internal/DefaultConfigurationContextChangeListener.java b/src/main/java/org/apache/tamaya/events/internal/DefaultConfigurationContextChangeListener.java index fed5e40..e49856d 100644 --- a/src/main/java/org/apache/tamaya/events/internal/DefaultConfigurationContextChangeListener.java +++ b/src/main/java/org/apache/tamaya/events/internal/DefaultConfigurationContextChangeListener.java @@ -19,13 +19,17 @@ package org.apache.tamaya.events.internal; import org.apache.tamaya.ConfigurationProvider; +import org.apache.tamaya.events.ConfigEvent; import org.apache.tamaya.events.ConfigEventListener; -import org.apache.tamaya.events.delta.ConfigurationContextChange; +import org.apache.tamaya.events.ConfigurationContextChange; import org.apache.tamaya.spi.ConfigurationContext; import org.apache.tamaya.spi.ConfigurationContextBuilder; import org.apache.tamaya.spi.PropertySource; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,35 +37,38 @@ import java.util.logging.Logger; * Default ConfigEventListener for ConfigurationContextChange events that updates the current context, if resources were * affected. */ -public class DefaultConfigurationContextChangeListener implements ConfigEventListener<ConfigurationContextChange> { +public class DefaultConfigurationContextChangeListener implements ConfigEventListener { private static final Logger LOG = Logger.getLogger(DefaultConfigurationContextChangeListener.class.getName()); @Override - public void onConfigEvent(ConfigurationContextChange event) { - ConfigurationContext context = ConfigurationProvider.getConfigurationContext(); - List<PropertySource> affectedPropertySources = new ArrayList<>(); - for (PropertySource ps : context.getPropertySources()) { - if (event.isAffected(ps)) { - affectedPropertySources.add(ps); + public void onConfigEvent(ConfigEvent<?> event) { + if(event.getClass() == ConfigurationContextChange.class) { + ConfigurationContextChange contextChange = (ConfigurationContextChange) event; + ConfigurationContext context = ConfigurationProvider.getConfigurationContext(); + List<PropertySource> affectedPropertySources = new ArrayList<>(); + for (PropertySource ps : context.getPropertySources()) { + if (contextChange.isAffected(ps)) { + affectedPropertySources.add(ps); + } } - } - ConfigurationContextBuilder newContextBuilder = ConfigurationProvider.getConfigurationContextBuilder() - .setContext(context); - if (!affectedPropertySources.isEmpty()) { - Set<String> propertySourceNames = new HashSet<>(); - for (PropertySource removed : event.getRemovedPropertySources()) { - propertySourceNames.add(removed.getName()); + ConfigurationContextBuilder newContextBuilder = ConfigurationProvider.getConfigurationContextBuilder() + .setContext(context); + if (!affectedPropertySources.isEmpty()) { + Set<String> propertySourceNames = new HashSet<>(); + for (PropertySource removed : contextChange.getRemovedPropertySources()) { + propertySourceNames.add(removed.getName()); + } + newContextBuilder.removePropertySources(propertySourceNames); + } + newContextBuilder.addPropertySources(contextChange.getAddedPropertySources()); + newContextBuilder.addPropertySources(contextChange.getUpdatedPropertySources()); + ConfigurationContext newContext = newContextBuilder.build(); + try { + ConfigurationProvider.setConfigurationContext(newContext); + } catch (Exception e) { + LOG.log(Level.INFO, "Failed to update the current ConfigurationContext due to config model changes", e); } - newContextBuilder.removePropertySources(propertySourceNames); - } - newContextBuilder.addPropertySources(event.getAddedPropertySources()); - newContextBuilder.addPropertySources(event.getUpdatedPropertySources()); - ConfigurationContext newContext = newContextBuilder.build(); - try { - ConfigurationProvider.setConfigurationContext(newContext); - } catch (Exception e) { - LOG.log(Level.INFO, "Failed to update the current ConfigurationContext due to config model changes", e); } } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/internal/LoggingConfigListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/internal/LoggingConfigListener.java b/src/main/java/org/apache/tamaya/events/internal/LoggingConfigListener.java index 43b2700..41d6924 100644 --- a/src/main/java/org/apache/tamaya/events/internal/LoggingConfigListener.java +++ b/src/main/java/org/apache/tamaya/events/internal/LoggingConfigListener.java @@ -18,20 +18,24 @@ */ package org.apache.tamaya.events.internal; -import org.apache.tamaya.events.ConfigListener; -import org.apache.tamaya.events.delta.ConfigurationChange; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.events.ConfigEvent; +import org.apache.tamaya.events.ConfigEventListener; +import org.apache.tamaya.events.ConfigurationChange; import java.util.logging.Logger; /** * Simple ConfigListener that simply logs any detected config changes to INFO level. */ -public class LoggingConfigListener implements ConfigListener{ +public class LoggingConfigListener implements ConfigEventListener { private static final Logger LOG = Logger.getLogger(LoggingConfigListener.class.getName()); @Override - public void onConfigEvent(ConfigurationChange event) { - LOG.info("Configuration changed: " + event); + public void onConfigEvent(ConfigEvent<?> event) { + if(event.getResourceType()== Configuration.class) { + LOG.info("Configuration changed: " + event); + } } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/package-info.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/package-info.java b/src/main/java/org/apache/tamaya/events/package-info.java index 33f5f25..e175ceb 100644 --- a/src/main/java/org/apache/tamaya/events/package-info.java +++ b/src/main/java/org/apache/tamaya/events/package-info.java @@ -18,6 +18,7 @@ */ /** * This package provides the main building blocks for handling configuration changes, such as - * {@link org.apache.tamaya.events.ConfigEventManager}, {@link org.apache.tamaya.events.ConfigEventListener}. + * {@link org.apache.tamaya.events.ConfigEventManager}, {@link org.apache.tamaya.events.ConfigEventListener} and + * artifacts to describe the changes (delta) of a Configuration or a PropertySource. */ package org.apache.tamaya.events; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/spi/BaseConfigEvent.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/spi/BaseConfigEvent.java b/src/main/java/org/apache/tamaya/events/spi/BaseConfigEvent.java new file mode 100644 index 0000000..15f3dfd --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/spi/BaseConfigEvent.java @@ -0,0 +1,69 @@ +/* + * 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.spi; + +import org.apache.tamaya.events.ConfigEvent; + +import java.util.Objects; +import java.util.UUID; + +/** + * Abstract base class for implementing your own configuration events. + * @param <T> the vent type + */ +public abstract class BaseConfigEvent<T> implements ConfigEvent<T> { + private long timestamp = System.currentTimeMillis(); + protected String version = UUID.randomUUID().toString(); + protected T paylod; + private Class<T> type; + + public BaseConfigEvent(T paylod, Class<T> type){ + this.paylod = Objects.requireNonNull(paylod); + this.type = Objects.requireNonNull(type); + } + + @Override + public Class<T> getResourceType() { + return type; + } + + @Override + public T getResource() { + return paylod; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public long getTimestamp() { + return timestamp; + } + + @Override + public String toString() { + return getClass().getSimpleName() + '{' + + "timestamp=" + timestamp + + ", version='" + version + '\'' + + ", paylod='" + paylod + '\'' + + '}'; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/spi/ConfigEventManagerSpi.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/spi/ConfigEventManagerSpi.java b/src/main/java/org/apache/tamaya/events/spi/ConfigEventManagerSpi.java index 7cb47a0..1ac1055 100644 --- a/src/main/java/org/apache/tamaya/events/spi/ConfigEventManagerSpi.java +++ b/src/main/java/org/apache/tamaya/events/spi/ConfigEventManagerSpi.java @@ -18,9 +18,11 @@ */ package org.apache.tamaya.events.spi; +import org.apache.tamaya.events.ConfigEvent; import org.apache.tamaya.events.ConfigEventListener; import java.util.Collection; +import java.util.Set; /** * SPI interface to implement the {@link org.apache.tamaya.events.ConfigEventManager} singleton. @@ -35,29 +37,88 @@ public interface ConfigEventManagerSpi { * * @param l the listener not null. */ - <T> void addListener(ConfigEventListener<T> l); + <T> void addListener(ConfigEventListener l); + /** + * Adds a listener for observing events of a given type. + * + * @param l the listener not null. + * @param eventType the type of concrete configuration event this listeners should be informed about. All other + * event types will never be delivered toe this listener instance. + */ + <T extends ConfigEvent> void addListener(ConfigEventListener l, Class<T> eventType); /** * Removes a listener for observing events. * * @param l the listener not null. */ - <T> void removeListener(ConfigEventListener<T> l); + void removeListener(ConfigEventListener l); + + /** + * Removes a listener for observing events of a certain type. + * + * @param l the listener not null. + * @param eventType the type of concrete configuration event this listeners should be informed about. All other + * event types will never be delivered toe this listener instance. + */ + <T extends ConfigEvent> void removeListener(ConfigEventListener l, Class<T> eventType); /** - * Publishes an event to all interested listeners. + * Access all globally registered listeners. * - * @param event the event, not null. - * @param eventType the event type. + * @return the listeners found, never null. + */ + Collection<? extends ConfigEventListener> getListeners(); + + /** + * Access all listeners listening for a certain event type, including any global listeners. + * @param eventType the type of concrete configuration event this listeners should be informed about. All other + * event types will never be delivered toe this listener instance. + * @return the listeners found, never null. */ - <T> void fireEvent(T event, Class<T> eventType); + Collection<? extends ConfigEventListener> getListeners(Class<? extends ConfigEvent> eventType); /** - * Access all known listeners for a given targe type. - * @param type the type. - * @param <T> the listener type. - * @return the items found, never null. + * Publishes an event to all interested listeners, hereby executing all registered listeners sequentually and + * synchronously., + * + * @param event the event, not null. */ - <T> Collection<? extends ConfigEventListener<T>> getListeners(Class<T> type); + void fireEvent(ConfigEvent<?> event); + + /** + * Publishes an event to all interested listeners, hereby publishing the change events asynchrously and in + * parallel (multithreaded). + * + * @param event the event, not null. + */ + void fireEventAsynch(ConfigEvent<?> event); + + /** + * Get the current check period to check for configuration changes. + * + * @return the check period in ms. + */ + long getChangeMonitoringPeriod(); + + void setChangeMonitoringPeriod(long millis); + + /** + * Check if the observer is running currently. + * + * @return true, if the change monitoring service is currently running. + */ + boolean isChangeMonitorActive(); + + /** + * Start/Stop the change monitoring service, which will observe/reevaluate the current configuration regularly + * and triggers ConfigurationChange events is 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 remotedly. FOr handling corresponding {@link ConfigEventListener} have + * to be registered, e.g. listening on {@link org.apache.tamaya.events.ConfigurationChange} events. + */ + void enableChangeMonitor(boolean enable); + + } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/spi/ConfigObserverSpi.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/spi/ConfigObserverSpi.java b/src/main/java/org/apache/tamaya/events/spi/ConfigObserverSpi.java deleted file mode 100644 index 95eb6e7..0000000 --- a/src/main/java/org/apache/tamaya/events/spi/ConfigObserverSpi.java +++ /dev/null @@ -1,71 +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.spi; - -import java.util.Collection; -import java.util.Set; - -/** - * SPI interface to implement the {@link org.apache.tamaya.events.ConfigurationObserver} singleton. - * Implementations of this interface must be registered with the current {@link org.apache.tamaya.spi.ServiceContext}, - * by default this equals to registering it with {@link java.util.ServiceLoader}. Add {@link javax.annotation.Priority} - * annotations for overriding (higher values overriden lower values). - */ -public interface ConfigObserverSpi { - /** - * Add key expressions for generating ConfigurationChange events. - * - * @param keys the keys to be observed for changes. - */ - <T> void addObservedKeys(Collection<String> keys); - - /** - * Remove key expressions for generating ConfigurationChange events. - * - * @param keys the keys to be observed for changes. - */ - <T> void removeObservedKeys(Collection<String> keys); - - /** - * Get all registered key expressions for generating ConfigurationChange events. - * - * @return a set with the keys found, never null. - */ - Set<String> getObservedKeys(); - - /** - * Get the current check period to check for configuration changes. - * - * @return the check period in ms. - */ - long getCheckPeriod(); - - /** - * Check if the observer is running currently. - * - * @return true, if the observer is running. - */ - boolean isRunning(); - - /** - * Start/Stop the observer container. - */ - void enableObservation(boolean enable); - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/resources/META-INF/services/org.apache.tamaya.events.ConfigEventListener ---------------------------------------------------------------------- diff --git a/src/main/resources/META-INF/services/org.apache.tamaya.events.ConfigEventListener b/src/main/resources/META-INF/services/org.apache.tamaya.events.ConfigEventListener index f675fd6..f9942c1 100644 --- a/src/main/resources/META-INF/services/org.apache.tamaya.events.ConfigEventListener +++ b/src/main/resources/META-INF/services/org.apache.tamaya.events.ConfigEventListener @@ -16,4 +16,4 @@ # specific language governing permissions and limitations # under the License. # -org.apache.tamaya.events.internal.DefaultConfigurationContextChangeListener \ No newline at end of file +org.apache.tamaya.events.internal.DefaultConfigurationContextChangeListener http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/resources/META-INF/services/org.apache.tamaya.events.spi.ConfigObserverSpi ---------------------------------------------------------------------- diff --git a/src/main/resources/META-INF/services/org.apache.tamaya.events.spi.ConfigObserverSpi b/src/main/resources/META-INF/services/org.apache.tamaya.events.spi.ConfigObserverSpi index de5ee6f..99670e3 100644 --- a/src/main/resources/META-INF/services/org.apache.tamaya.events.spi.ConfigObserverSpi +++ b/src/main/resources/META-INF/services/org.apache.tamaya.events.spi.ConfigObserverSpi @@ -16,4 +16,4 @@ # specific language governing permissions and limitations # under the License. # -org.apache.tamaya.events.internal.DefaultConfigObserverSpi \ No newline at end of file +org.apache.tamaya.events.internal.DefaultConfigChangeObserver \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/test/java/org/apache/tamaya/events/ChangeableGlobalPropertySource.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/tamaya/events/ChangeableGlobalPropertySource.java b/src/test/java/org/apache/tamaya/events/ChangeableGlobalPropertySource.java new file mode 100644 index 0000000..c93b2ca --- /dev/null +++ b/src/test/java/org/apache/tamaya/events/ChangeableGlobalPropertySource.java @@ -0,0 +1,62 @@ +/* + * 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.core.propertysource.BasePropertySource; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * PropertySource implementation that accesses properties that are statically stored. + */ +public class ChangeableGlobalPropertySource extends BasePropertySource{ + + private static Map<String,String> STORED_ENTRIES = new ConcurrentHashMap<>(); + + @Override + public String getName() { + return getClass().getSimpleName(); + } + + @Override + public Map<String, String> getProperties() { + return null; + } + + /** + * Put a value (globally) into this property source. + * @param key the key, not null + * @param value the value, not null + * @return the entry replaced, or null. + */ + public static String put(String key, String value){ + return STORED_ENTRIES.put(key,value); + } + + /** + * Put all the properties, overriding any existing ones with the same key. + * @param properties the properties, not null. + */ + public static void putAll(Map<String,String> properties){ + STORED_ENTRIES.putAll(properties); + } + +}