Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id E3ECD200BB4 for ; Tue, 27 Sep 2016 00:19:01 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id E29AE160AE3; Mon, 26 Sep 2016 22:19:01 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 99AC1160AE5 for ; Tue, 27 Sep 2016 00:18:59 +0200 (CEST) Received: (qmail 8881 invoked by uid 500); 26 Sep 2016 22:18:58 -0000 Mailing-List: contact commits-help@tamaya.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@tamaya.incubator.apache.org Delivered-To: mailing list commits@tamaya.incubator.apache.org Received: (qmail 8872 invoked by uid 99); 26 Sep 2016 22:18:58 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 26 Sep 2016 22:18:58 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 465B61A539F for ; Mon, 26 Sep 2016 22:18:58 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.646 X-Spam-Level: X-Spam-Status: No, score=-4.646 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-1.426] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id XLjvoC-KzvxB for ; Mon, 26 Sep 2016 22:18:48 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 2E75B60E57 for ; Mon, 26 Sep 2016 22:18:44 +0000 (UTC) Received: (qmail 6408 invoked by uid 99); 26 Sep 2016 22:18:42 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 26 Sep 2016 22:18:42 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id B0A58E188D; Mon, 26 Sep 2016 22:18:42 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: plexus@apache.org To: commits@tamaya.incubator.apache.org Date: Mon, 26 Sep 2016 22:19:03 -0000 Message-Id: In-Reply-To: <93d883605ebd4940a925bf14b926c39a@git.apache.org> References: <93d883605ebd4940a925bf14b926c39a@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [22/50] [abbrv] incubator-tamaya-extensions git commit: Simplified events module, adapted documentation as well. Removed model dependency from events, since events is the more general module here. archived-at: Mon, 26 Sep 2016 22:19:02 -0000 Simplified events module, adapted documentation as well. Removed model dependency from events, since events is the more general module here. 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/384b09eb Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/tree/384b09eb Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/diff/384b09eb Branch: refs/heads/master Commit: 384b09eb8cc3dca3d4699a4353b4f6a420904623 Parents: c87ace0 Author: anatole Authored: Wed Dec 16 08:37:17 2015 +0100 Committer: Oliver B. Fischer Committed: Tue Sep 27 00:18:31 2016 +0200 ---------------------------------------------------------------------- .../tamaya/events/ChangeNotification.java | 66 ----- .../org/apache/tamaya/events/ChangeType.java | 28 ++ .../org/apache/tamaya/events/ConfigEvent.java | 58 ++++ .../tamaya/events/ConfigEventListener.java | 5 +- .../tamaya/events/ConfigEventManager.java | 112 ++++++-- .../apache/tamaya/events/ConfigListener.java | 31 --- .../tamaya/events/ConfigurationChange.java | 222 +++++++++++++++ .../events/ConfigurationChangeBuilder.java | 272 +++++++++++++++++++ .../events/ConfigurationContextChange.java | 208 ++++++++++++++ .../ConfigurationContextChangeBuilder.java | 173 ++++++++++++ .../tamaya/events/ConfigurationObserver.java | 107 -------- .../tamaya/events/PropertySourceChange.java | 242 +++++++++++++++++ .../events/PropertySourceChangeBuilder.java | 258 ++++++++++++++++++ .../apache/tamaya/events/delta/ChangeType.java | 28 -- .../events/delta/ConfigurationChange.java | 216 --------------- .../delta/ConfigurationChangeBuilder.java | 262 ------------------ .../delta/ConfigurationContextChange.java | 192 ------------- .../ConfigurationContextChangeBuilder.java | 159 ----------- .../events/delta/PropertySourceChange.java | 236 ---------------- .../delta/PropertySourceChangeBuilder.java | 258 ------------------ .../folderobserver/FileChangeListener.java | 3 +- .../ObservingPropertySourceProvider.java | 8 +- .../internal/DefaultConfigChangeObserver.java | 107 ++++++++ .../internal/DefaultConfigEventManagerSpi.java | 162 ++++++++--- .../internal/DefaultConfigObserverSpi.java | 158 ----------- ...faultConfigurationContextChangeListener.java | 57 ++-- .../events/internal/LoggingConfigListener.java | 14 +- .../org/apache/tamaya/events/package-info.java | 3 +- .../tamaya/events/spi/BaseConfigEvent.java | 69 +++++ .../events/spi/ConfigEventManagerSpi.java | 83 +++++- .../tamaya/events/spi/ConfigObserverSpi.java | 71 ----- ...org.apache.tamaya.events.ConfigEventListener | 2 +- ...g.apache.tamaya.events.spi.ConfigObserverSpi | 2 +- .../events/ChangeableGlobalPropertySource.java | 62 +++++ .../ChangeableThreadLocalPropertySource.java | 57 ++++ .../tamaya/events/ConfigEventManagerTest.java | 25 +- .../org/apache/tamaya/events/SimpleEvent.java | 13 + .../events/delta/ConfigurationChangeTest.java | 4 +- .../delta/ConfigurationContextChangeTest.java | 9 +- .../events/delta/PropertySourceChangeTest.java | 5 +- .../DefaultConfigEventManagerSpiTest.java | 19 +- 41 files changed, 2151 insertions(+), 1915 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ChangeNotification.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ChangeNotification.java b/src/main/java/org/apache/tamaya/events/ChangeNotification.java deleted file mode 100644 index 73a4fa9..0000000 --- a/src/main/java/org/apache/tamaya/events/ChangeNotification.java +++ /dev/null @@ -1,66 +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 java.beans.PropertyChangeEvent; -import java.util.Collection; - - -/** - * Event that contains a set current changes that were applied or could be applied. - * @param the event type - */ -public interface ChangeNotification{ - - /** - * Get the underlying property provider/configuration. - * @return the underlying property provider/configuration, never null. - */ - T getResource(); - - /** - * Get the version relative to the observed resource. The version is required to be unique for - * each change emmitted for a resource. There is no further requirement how this uniqueness is - * modelled, so returning a UUID is cometely valid. - * @return the base version. - */ - String getVersion(); - - /** - * Get the timestamp in millis from the current epoch. it is expected that the timestamp and the version are unique to - * identify a changeset. - * @return the timestamp, when this changeset was created. - */ - long getTimestamp(); - - /** - * Get the changes recorded. - * @return the recorded changes, never null. - */ - Collection getChanges(); - - /** - * Checks if the given key is added, or updated OR removed. - * @param key the target key (can also be a regular expression), that matches the requested keys, - * not null. - * @return true, if the given key was added, or updated BUT NOT removed. - */ - boolean isKeyAffected(String key); - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ChangeType.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ChangeType.java b/src/main/java/org/apache/tamaya/events/ChangeType.java new file mode 100644 index 0000000..4363579 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/ChangeType.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * Created by Anatole on 20.02.2015. + */ +public enum ChangeType { + NEW, + DELETED, + UPDATED, +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ConfigEvent.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigEvent.java b/src/main/java/org/apache/tamaya/events/ConfigEvent.java new file mode 100644 index 0000000..b56145e --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/ConfigEvent.java @@ -0,0 +1,58 @@ +/* + * 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 java.beans.PropertyChangeEvent; +import java.util.Collection; + + +/** + * Event that contains a set current changes that were applied or could be applied. + * @param the resource type. + */ +public interface ConfigEvent{ + + /** + * Access the type of resource. This allows to easily determine the resource an event wants to observe. + * @return the resource type. + */ + Class getResourceType(); + + /** + * Get the underlying property provider/configuration. + * @return the underlying property provider/configuration, never null. + */ + T getResource(); + + /** + * Get the version relative to the observed resource. The version is required to be unique for + * each change emmitted for a resource. There is no further requirement how this uniqueness is + * modelled, so returning a UUID is a completely valid strategy. + * @return the base version. + */ + String getVersion(); + + /** + * Get the timestamp in millis from the current epoch. it is expected that the timestamp and the version are unique to + * identify a changeset. + * @return the timestamp, when this changeset was created. + */ + long getTimestamp(); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ConfigEventListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigEventListener.java b/src/main/java/org/apache/tamaya/events/ConfigEventListener.java index dde5175..de635f8 100644 --- a/src/main/java/org/apache/tamaya/events/ConfigEventListener.java +++ b/src/main/java/org/apache/tamaya/events/ConfigEventListener.java @@ -20,14 +20,13 @@ package org.apache.tamaya.events; /** * Interface to be implemented for listening on changes on {@link org.apache.tamaya.Configuration} instances. - * @param the type listened to. */ //@FunctionalInterface -public interface ConfigEventListener { +public interface ConfigEventListener { /** * Called if an event occurred. * @param event the event, not null. */ - void onConfigEvent(T event); + void onConfigEvent(ConfigEvent event); } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ConfigEventManager.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigEventManager.java b/src/main/java/org/apache/tamaya/events/ConfigEventManager.java index 1095d48..f0bd0fa 100644 --- a/src/main/java/org/apache/tamaya/events/ConfigEventManager.java +++ b/src/main/java/org/apache/tamaya/events/ConfigEventManager.java @@ -42,12 +42,23 @@ public final class ConfigEventManager { } /** - * Add a listener for observing change events on {@link org.apache.tamaya.Configuration}. References of this - * component to the listeners must be managed as weak references. - * + * Adds a Config listener that listens to all kind of {@link ConfigEvent}. + * @param l the listener not null. + */ + public static void addListener(ConfigEventListener l) { + if (SPI == null) { + throw new ConfigException("No SPI registered for " + + ConfigEventManager.class.getName()); + } + SPI.addListener(l); + } + + /** + * Adds a Config listener that listens to all kind of {@link ConfigEvent}. * @param l the listener not null. + * @param eventType the event type to which this listener listens to. */ - public static void addListener(ConfigEventListener l) { + public static void addListener(ConfigEventListener l, Class eventType) { if (SPI == null) { throw new ConfigException("No SPI registered for " + ConfigEventManager.class.getName()); @@ -56,12 +67,25 @@ public final class ConfigEventManager { } /** - * Add a listener for observing change events on {@link org.apache.tamaya.spi.PropertySource}. References of this - * component to the listeners must be managed as weak references. + * Removes a listener registered globally. + * + * @param l the listener not null. + */ + public static void removeListener(ConfigEventListener l) { + if (SPI == null) { + throw new ConfigException("No SPI registered for " + + ConfigEventManager.class.getName()); + } + SPI.removeListener(l); + } + + /** + * Removes a listener registered for the given event type. * * @param l the listener not null. + * @param eventType the event type to which this listener listens to. */ - public static void removeListener(ConfigEventListener l) { + public static void removeListener(ConfigEventListener l, Class eventType) { if (SPI == null) { throw new ConfigException("No SPI registered for " + ConfigEventManager.class.getName()); @@ -70,38 +94,84 @@ public final class ConfigEventManager { } /** - * Access all registered ConfigEventListeners listening to the given event type. + * Access all registered ConfigEventListeners listening to a given event type. * @param type the event type * @param type param * @return a list with the listeners found, never null. */ - public static - Collection> getListeneters(Class type) { + public static + Collection getListeners(Class type) { return SPI.getListeners(type); } + /** + * Access all registered ConfigEventListeners listening to a all kind of event types globally. + * @return a list with the listeners found, never null. + */ + public static + Collection getListeners() { + return SPI.getListeners(); + } /** - * Publishes sn event to all interested listeners. + * Publishes a {@link ConfigurationChange} synchronously to all interested listeners. * * @param event the event, not null. */ - public static void fireEvent(Object event) { - fireEvent(event, (Class)event.getClass()); + public static void fireEvent(ConfigEvent event) { + SPI.fireEvent(event); } /** - * Publishes a {@link org.apache.tamaya.events.delta.ConfigurationChange} to all interested listeners. + * Publishes a {@link ConfigurationChange} asynchronously/multithreaded to all interested listeners. * * @param event the event, not null. - * @param eventType the event type, the vent may be a subclass. */ - public static void fireEvent(T event, Class eventType) { - if (SPI == null) { - throw new ConfigException("No SPI registered for " + - ConfigEventManager.class.getName()); - } - SPI.fireEvent(event, eventType); + public static void fireEventAsynch(ConfigEvent event) { + SPI.fireEventAsynch(event); + } + + /** + * 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. + * @see #isChangeMonitoring() + * @see #getChangeMonitoringPeriod() + */ + public static void enableChangeMonitoring(boolean enable) { + SPI.enableChangeMonitor(enable); + } + + /** + * Check if the observer is running currently. + * + * @return true, if the change monitoring service is currently running. + * @see #enableChangeMonitoring(boolean) + */ + public static boolean isChangeMonitoring() { + return SPI.isChangeMonitorActive(); + } + + /** + * Get the current check period to check for configuration changes. + * + * @return the check period in ms. + */ + public long getChangeMonitoringPeriod(){ + return SPI.getChangeMonitoringPeriod(); + } + + /** + * Sets the current monitoring period and restarts the monitor. You still have to enable the monitor if + * it is currently not enabled. + * @param millis + * @see #enableChangeMonitoring(boolean) + * @see #isChangeMonitoring() + */ + public void setChangeMonitoringPeriod(long millis){ + SPI.setChangeMonitoringPeriod(millis); } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ConfigListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigListener.java b/src/main/java/org/apache/tamaya/events/ConfigListener.java deleted file mode 100644 index 3608921..0000000 --- a/src/main/java/org/apache/tamaya/events/ConfigListener.java +++ /dev/null @@ -1,31 +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.events.delta.ConfigurationChange; - -/** - * Simple observer interface that can be registered using the current {@code ServiceContext}. - * This class will be called on each configuration change detected in the current environment. - */ -// @FunctionalInterface -public interface ConfigListener - extends ConfigEventListener { - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ConfigurationChange.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigurationChange.java b/src/main/java/org/apache/tamaya/events/ConfigurationChange.java new file mode 100644 index 0000000..a291084 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/ConfigurationChange.java @@ -0,0 +1,222 @@ +/* + * 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, Serializable{ + + private static final long serialVersionUID = 1L; + /** The base property provider/configuration. */ + private 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 Map 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 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 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 && +// e.getNewValue() != 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 "ConfigurationChange{" + + "configuration=" + configuration + + ", version='" + version + '\'' + + ", timestamp=" + timestamp + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java b/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java new file mode 100644 index 0000000..78f60a9 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/ConfigurationChangeBuilder.java @@ -0,0 +1,272 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tamaya.events; + +import org.apache.tamaya.Configuration; +import org.apache.tamaya.ConfigurationProvider; + +import java.beans.PropertyChangeEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Models a set current changes applied to a {@link org.apache.tamaya.spi.PropertySource}. Consumers of these events + * can observing changes to property sources and + *
    + *
  1. 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).
  2. + *
  3. 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. + *
+ */ +public final class ConfigurationChangeBuilder { + /** + * The recorded changes. + */ + final SortedMap 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 using the current COnfiguration as root resource. + * + * @return the builder for chaining. + */ + public static ConfigurationChangeBuilder of() { + return new ConfigurationChangeBuilder(ConfigurationProvider.getConfiguration()); + } + + /** + * Creates a new instance current this builder. + * + * @param configuration the configuration changed, not null. + * @return the builder for chaining. + */ + public static ConfigurationChangeBuilder of(Configuration configuration) { + return new ConfigurationChangeBuilder(configuration); + } + + /** + * Compares the two property config/configurations and creates a collection current all changes + * that must be appied to render {@code map1} into {@code map2}. + * + * @param map1 the source map, not null. + * @param map2 the target map, not null. + * @return a collection current change events, never null. + */ + public static Collection compare(Configuration map1, Configuration map2) { + List changes = new ArrayList<>(); + for (Map.Entry 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 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 + *
    + *
  • the registered codecs provider provides codecs for the corresponding keys, or
  • + *
  • default codecs are present for the given type, or
  • + *
  • the value is an instanceof String
  • + *
+ * + * @param changes the changes to be applied, not null. + * @return the builder for chaining. + */ + public ConfigurationChangeBuilder putAll(Map 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 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/ConfigurationContextChange.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigurationContextChange.java b/src/main/java/org/apache/tamaya/events/ConfigurationContextChange.java new file mode 100644 index 0000000..eda5ab1 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/ConfigurationContextChange.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tamaya.events; + +import org.apache.tamaya.spi.ConfigurationContext; +import org.apache.tamaya.spi.PropertySource; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Event that contains a set 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 ConfigurationContextChange implements ConfigEvent, Serializable{ + + private static final long serialVersionUID = 1L; + /** The base property provider/configuration. */ + private List changedPropertySources = new ArrayList<>(); + /** The base version, usable for optimistic locking. */ + private String version = UUID.randomUUID().toString(); + /** The timestamp of the change set in millis from the epoch. */ + private long timestamp = System.currentTimeMillis(); + /** The configuration context. */ + private ConfigurationContext configurationContext; + + /** + * Get an empty change set for the given provider. + * @return an empty ConfigurationContextChange instance. + */ + public static ConfigurationContextChange emptyChangeSet(ConfigurationContext configurationContext){ + return ConfigurationContextChangeBuilder.of(configurationContext).build(); + } + + /** + * Constructor used by {@link PropertySourceChangeBuilder}. + * @param builder The builder used, not null. + */ + ConfigurationContextChange(ConfigurationContextChangeBuilder builder) { + this.changedPropertySources.addAll(builder.changedPropertySources); + if(builder.version!=null){ + this.version = builder.version; + } + if(builder.timestamp!=null){ + this.timestamp = builder.timestamp; + } + this.configurationContext = builder.configurationContext; + } + + @Override + public Class getResourceType() { + return ConfigurationContext.class; + } + + @Override + public ConfigurationContext getResource() { + return configurationContext; + } + + /** + * Get the base version, usable for optimistic locking. + * @return the base version. + */ + @Override + public String getVersion(){ + return version; + } + + /** + * Get the timestamp in millis from the current epoch. it is expected that the timestamp and the version are unique to + * identify a changeset. + * @return the timestamp, when this changeset was created. + */ + @Override + public long getTimestamp(){ + return timestamp; + } + + /** + * Get the changes recorded. + * @return the recorded changes, never null. + */ + public Collection getPropertySourceChanges(){ + return Collections.unmodifiableCollection(this.changedPropertySources); + } + + /** + * Get the property source updates. + * @return the recorded changes, never null. + */ + public Collection getPropertySourceUpdates(){ + List 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 getRemovedPropertySources(){ + List 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 getAddedPropertySources(){ + List 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 getUpdatedPropertySources(){ + List 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/ConfigurationContextChangeBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigurationContextChangeBuilder.java b/src/main/java/org/apache/tamaya/events/ConfigurationContextChangeBuilder.java new file mode 100644 index 0000000..2341f92 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/ConfigurationContextChangeBuilder.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tamaya.events; + +import org.apache.tamaya.ConfigurationProvider; +import org.apache.tamaya.spi.ConfigurationContext; +import org.apache.tamaya.spi.PropertySource; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Models a set current changes applied to a {@link org.apache.tamaya.spi.PropertySource}. Consumers of these events + * can observing changes to property sources and + *
    + *
  1. 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).
  2. + *
  3. 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. + *
+ */ +public final class ConfigurationContextChangeBuilder { + /** + * The recorded changes. + */ + final List changedPropertySources = new ArrayList<>(); + /** + * The version configured, or null, for generating a default. + */ + String version; + /** + * The optional timestamp in millis of this epoch. + */ + Long timestamp; + + ConfigurationContext configurationContext; + + /** + * Constructor. + */ + private ConfigurationContextChangeBuilder(ConfigurationContext configurationContext) { + this.configurationContext = Objects.requireNonNull(configurationContext); + } + + /** + * Just creates a new ConfigurationContextBuilder using the current COnfigurationContext has root resource. + * @return a new ConfigurationContextBuilder, never null. + */ + public static ConfigurationContextChangeBuilder of() { + return of(ConfigurationProvider.getConfigurationContext()); + } + + /** + * Creates a new instance current this builder. + * + * @return the builder for chaining. + */ + public static ConfigurationContextChangeBuilder of(ConfigurationContext context) { + return new ConfigurationContextChangeBuilder(context); + } + + /* + * Apply a version/UUID to the set being built. + * @param version the version to apply, or null, to let the system generate a version for you. + * @return the builder for chaining. + */ + public ConfigurationContextChangeBuilder setVersion(String version) { + this.version = version; + return this; + } + + /* + * Apply given timestamp to the set being built. + * @param 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/ConfigurationObserver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/ConfigurationObserver.java b/src/main/java/org/apache/tamaya/events/ConfigurationObserver.java deleted file mode 100644 index 1eafc0c..0000000 --- a/src/main/java/org/apache/tamaya/events/ConfigurationObserver.java +++ /dev/null @@ -1,107 +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.events.spi.ConfigObserverSpi; -import org.apache.tamaya.spi.ServiceContextManager; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Set; - -/** - * Singleton accessor for managing {@link ConfigListener} instances and mappings. - */ -public class ConfigurationObserver { - - /** - * Private singleton constructor. - */ - private ConfigurationObserver() { - } - - /** - * The backing SPI. - */ - private static final ConfigObserverSpi SPI = ServiceContextManager.getServiceContext() - .getService(ConfigObserverSpi.class); - - - /** - * Add key expressions for generating ConfigurationChange events. - * - * @param keys the keys to be observed for changes. - */ - public static void addObservedKeys(Collection keys) { - if (SPI == null) { - throw new ConfigException("No SPI registered for " + - ConfigurationObserver.class.getName()); - } - SPI.addObservedKeys(keys); - } - - /** - * Add key expressions for generating ConfigurationChange events. - * - * @param keys the keys to be observed for changes. - */ - public static void addObservedKeys(String... keys) { - if (SPI == null) { - throw new ConfigException("No SPI registered for " + - ConfigurationObserver.class.getName()); - } - SPI.addObservedKeys(Arrays.asList(keys)); - } - - /** - * Removes key expressions for generating ConfigurationChange events. - * - * @param keys the keys to be observed for changes. - */ - public static void removeObservedKeys(Collection keys) { - if (SPI == null) { - throw new ConfigException("No SPI registered for " + - ConfigurationObserver.class.getName()); - } - SPI.removeObservedKeys(keys); - } - - /** - * Removes key expressions for generating ConfigurationChange events. - * - * @param keys the keys to be observed for changes. - */ - public static void removeObservedKeys(String... keys) { - if (SPI == null) { - throw new ConfigException("No SPI registered for " + - ConfigurationObserver.class.getName()); - } - SPI.removeObservedKeys(Arrays.asList(keys)); - } - - /** - * Get all registered key expressions for generating ConfigurationChange events. - * - * @return set with the keys found, never null. - */ - public static Set getObservedKeys() { - return SPI.getObservedKeys(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/PropertySourceChange.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/PropertySourceChange.java b/src/main/java/org/apache/tamaya/events/PropertySourceChange.java new file mode 100644 index 0000000..a34e949 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/PropertySourceChange.java @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tamaya.events; + +import org.apache.tamaya.spi.PropertySource; + +import java.beans.PropertyChangeEvent; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Event that contains a set current changes that were applied or could be applied. + * This class is immutable and thread-safe. To create instances use + * {@link PropertySourceChangeBuilder}. + * + * Created by Anatole on 22.10.2014. + */ +public final class PropertySourceChange implements ConfigEvent, 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 changes = new HashMap<>(); + /** The overall type of change. */ + private ChangeType changeType; + + /** + * Constructor used by {@link PropertySourceChangeBuilder}. + * @param builder The builder used, not null. + */ + PropertySourceChange(PropertySourceChangeBuilder builder) { + this.propertySource = FrozenPropertySource.of(builder.source); + for (PropertyChangeEvent c : builder.delta.values()) { + this.changes.put(c.getPropertyName(), c); + } + if(builder.version!=null){ + this.version = builder.version; + } + if(builder.timestamp!=null){ + this.timestamp = builder.timestamp; + } + this.changeType = builder.changeType; + } + + /** + * Gets the type of change for this PropertySource. + * @return the type of change for this PropertySource, never null. + */ + public ChangeType getChangeType(){ + return this.changeType; + } + + @Override + public Class getResourceType() { + return PropertySource.class; + } + + /** + * Get the underlying property provider/configuration. + * @return the underlying property provider/configuration, or null, if the change instance was deserialized. + */ + @Override + public PropertySource getResource(){ + return this.propertySource; + } + + /** + * Get the base version, usable for optimistic locking. + * @return the base version. + */ + @Override + public String getVersion(){ + return version; + } + + /** + * Get the timestamp in millis from the current epoch. it is expected that the timestamp and the version are unique to + * identify a changeset. + * @return the timestamp, when this changeset was created. + */ + @Override + public long getTimestamp(){ + return timestamp; + } + + /** + * Get the changes recorded. + * @return the recorded changes, never null. + */ + public Collection 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/PropertySourceChangeBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/PropertySourceChangeBuilder.java b/src/main/java/org/apache/tamaya/events/PropertySourceChangeBuilder.java new file mode 100644 index 0000000..25f3620 --- /dev/null +++ b/src/main/java/org/apache/tamaya/events/PropertySourceChangeBuilder.java @@ -0,0 +1,258 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tamaya.events; + +import org.apache.tamaya.spi.PropertySource; + +import java.beans.PropertyChangeEvent; +import java.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 + *
    + *
  1. 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).
  2. + *
  3. 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. + *
+ */ +public final class PropertySourceChangeBuilder { + /** + * The recorded changes. + */ + final SortedMap 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 compare(PropertySource map1, PropertySource map2) { + List changes = new ArrayList<>(); + for (Map.Entry 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 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 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 + *
    + *
  • the registered codecs provider provides codecs for the corresponding keys, or
  • + *
  • default codecs are present for the given type, or
  • + *
  • the value is an instanceof String
  • + *
+ * + * @param changes the changes to be applied, not null. + * @return the builder for chaining. + */ + public PropertySourceChangeBuilder putAll(Map 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 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/delta/ChangeType.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/delta/ChangeType.java b/src/main/java/org/apache/tamaya/events/delta/ChangeType.java deleted file mode 100644 index ecc0a2d..0000000 --- a/src/main/java/org/apache/tamaya/events/delta/ChangeType.java +++ /dev/null @@ -1,28 +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; - -/** - * Created by Anatole on 20.02.2015. - */ -public enum ChangeType { - NEW, - DELETED, - UPDATED, -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/384b09eb/src/main/java/org/apache/tamaya/events/delta/ConfigurationChange.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/events/delta/ConfigurationChange.java b/src/main/java/org/apache/tamaya/events/delta/ConfigurationChange.java deleted file mode 100644 index 41c6ba1..0000000 --- a/src/main/java/org/apache/tamaya/events/delta/ConfigurationChange.java +++ /dev/null @@ -1,216 +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 org.apache.tamaya.events.ChangeNotification; -import org.apache.tamaya.events.FrozenConfiguration; - -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 ChangeNotification, Serializable{ - - private static final long serialVersionUID = 1L; - /** The base property provider/configuration. */ - private 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 Map 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; - } - } - - /** - * Get the underlying property provider/configuration. - * @return the underlying property provider/configuration, never null. - */ - public Configuration getResource(){ - return this.configuration; - } - - /** - * 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 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 && -// e.getNewValue() != 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 "ConfigurationChange{" + - "configuration=" + configuration + - ", version='" + version + '\'' + - ", timestamp=" + timestamp + - '}'; - } -}