You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2014/11/26 19:07:15 UTC
[10/11] incubator-tamaya git commit: Initial import from GitHub.
Added PGP public key.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/asciidoc/tasks.adoc
----------------------------------------------------------------------
diff --git a/api/src/main/asciidoc/tasks.adoc b/api/src/main/asciidoc/tasks.adoc
new file mode 100644
index 0000000..2bfb3ef
--- /dev/null
+++ b/api/src/main/asciidoc/tasks.adoc
@@ -0,0 +1,399 @@
+Apache Tamaya - Possible Tasks
+==============================
+:name: Tamaya
+:rootpackage: org.apache.tamaya
+:title: Apache Tamaya
+:revnumber: 0.1-SNAPSHOT
+:revremark: Draft
+:revdate: October 2014
+:longversion: {revnumber} ({revremark}) {revdate}
+:authorinitials: ATR
+:author: Anatole Tresch
+:email: <at...@gmail.com>
+:source-highlighter: coderay
+:website: http://tamaya.apache.org/
+:iconsdir: {imagesdir}/icons
+:toc:
+:toc-placement: manual
+:icons:
+:encoding: UTF-8
+:numbered:
+
+'''
+
+<<<
+
+-> add image : : https://raw.githubusercontent.com/JavaConfig/config-api/master/src/main/asciidoc/images/javaconfig.jpg[]
+
+toc::[]
+
+<<<
+:numbered!:
+-----------------------------------------------------------
+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.
+-----------------------------------------------------------
+
+:numbered:
+
+<<<
+
+== Introduction
+
+== What is Tamaya
+
+{name} is the Apache standard for flexible and powerful configuration. Objective is to provide flavors for
+Java SE, ME as well as to ship with powerful features for Java EE and Cloud Solutions. All functions provided
+is build on top of a small but very powerful, flexible and extendible API. This API is implemented by a core implementation,
+which then can be extended or adapted for use in different runtime scenarios, such as SE, ME, EE, Spring, OSGI
+and more. Similarly additional modules may be provided that help also existing solution to be plugged into
+{name}, so you can start right away using {name} without having to rebuild/change your existing application.
+
+== Main Features of {name}
+
+The main features of {name} currently are
+
+* *A simple key/value store*, named +PropertyProvider+ (simply called "provider").
+* Simple *built-in meta-data model*, that allows to easily attach metadata to a provider or single property keys.
+* Support for *different configuration formats*.
+* Support for *different configuration locations*, including remote locations.
+* Powerful and flexible *options to combine providers to new composite providers* using different combination policies.
+* The *Configuration model* adds additional features:
+** *Type Adapters* allow to convert configuration into any required target type.
+** *Built-in support* for all basic Java types.
+* *Contextual, hierarchical and multi-layered environment model*. The model is rather simple, but nevertheless
+ powerful enough to map complex layered runtime environments such as Java EE or multi-tenant SaaS solutions.
+* *Configurable System Properties* allow to tweak system properties to behave contextually.
+* *Templates* provide a type safe configuration mechanism where an annotated interface is implemented by the
+ configuration system, providing data from an underlying configuration.
+* *Configuration Injection* allows configured values to be directly injected into the bean's configured.
+* *Loading Policies* allow to control how configuration changes are reflected on the configured beans.
+* *Configuration Operators and Queries* can be used to easily implement advanced features such as *Views,
+ Security Constraints and Filters*.
+* Provider and configuration changes can be observed by registering *PropertyChangeListeners*.
+* Configurations are *versioned*, so remote pull scenarios can be implemented very efficiently.
+* The system supports *multiple configurations* identified by name.
+* The configuration system provides a powerful management console for reading and updating of configuration.
+* The system also supports distributed configuration scenarios by leveraging existing solutions, such as Memcached,
+ Hazelcast or Zookeper.
+* The system is built on "Java 8 features*.
+* A *Java 7 Backport* is provided.
+
+=== Purpose of this Document
+
+The document should help to organize people and ideas around the Apache Tamaya Library. It list possible features,
+ideas and tasks that need to be done. Everybody can have a look at and see, where hos contribution and capabilities
+would fit best.
+
+== Basics
+
+=== Styles, Logo
+
+The project requires
+
+* a good Apache styled logo and
+* CSS styles as needed,
+* an initial web page,
+* a twitter account
+* ...
+
+=== Infrastructure
+
+We should setup all needed infrastructure
+* code repos
+* project modules (including module sites)
+* coding and documentation guidelines
+* automatic builds (CI), included automatic coverage and sonar quality checks.
+* a docker image or appliance, with everything setup, so contributors can easily
+ start contributing...
+* ...
+
+== Main Features
+
+=== Metadata Model
+
+Currently +MetaInfo+ models metadata as a separate constuct. It has been shown that this leads to more complex
+handling when creating composites and makes the API overall more complex. The idea is to model metadata as simple
+key/value pairs, that are part of the provider/configuration data as well, but handled separately. Metadata hereby
+is identified by a starting '_' character in its key. For example refer to the following configuration properties:
+
+[source,listing]
+.Basic Properties
+----------------------------------------------------------------
+a.b.Foo=foo
+a.b.Bar=bar
+a.AnyOther=whatelse
+Something=none
+----------------------------------------------------------------
+
+Now we can model meta-data as follows:
+
+[source,listing]
+.Metadata Properties
+----------------------------------------------------------------
+[a.b].info=An area info
+[a.b.Foo].auth=role1,role2
+[a.b.Foo].encrypt=PGP
+[a.b.Foo].sensitive=true
+[].info=This is a test configuration example.
+----------------------------------------------------------------
+
+The above would model the following:
+
+* The area +a.b+ has the meta property +info+.
+* The entry +a.b.Foo+ has three meta properties +auth,encrypt+ and +sensitive+. These could be interpreted by a security
+ view and used to encrypt the values returned by the configuration instance, if not the current user has one of the
+ specified roles.
+* The last meta data defines an attribute +info+ for the whole provider/configuration (the root area).
+
+Given that the overall entries would be as follows:
+
+[source,listing]
+.Full Properties with Meta Properties
+----------------------------------------------------------------
+[a.b].info=An area info
+a.b.Foo=foo
+[a.b.Foo].auth=role1,role2
+[a.b.Foo].encrypt=PGP
+[a.b.Foo].sensitive=true
+a.b.Bar=bar
+[].info=This is a test configuration example.
+a.AnyOther=whatelse
+Something=none
+----------------------------------------------------------------
+
+The current +MetaInfo+ class could be adapted, so it is reading data from the underlying configuration/provider,
+instead of its own datastructure. This would make a later mapping of configuration and its metadata into DB table, JSON
+etc, much more easier.
+The providers on the other side may suppress any metadata from ordinary output, such
+as +toString()+, Similarly accessing metadata using the official config API (+get, getOrDefault, getAreas+ etc)
+should be disabled. The +MetaInfoBuilder+ must probably as well adapted or redesigned.
+
+=== Collection Support
+
+Add a key/value based model for mapping collections such as sets, maps, list. Implement according adapters.
+In combination with the metadata model above this could be something like:
+
+[source,listing]
+.Collection Support
+----------------------------------------------------------------
+mySet=[a,b,c,d,e\,e,f]
+[mySet].type=set
+#optional define the implementation class
+[mySet].class=java.util.TreeSet
+
+myList=[a,b,c,d,e\,e,f]
+[myList].type=list
+#optional define the implementation class
+[myList].class=java.util.ArrayList
+
+myMap=[a:aa,b:bb,c:cc,d:dd,e:e\,e,f:ff]
+[myMap].type=map
+#optional define the implementation class
+[myMap].class=java.util.TreeMap
+
+#Finally we could also add support for non String based types
+myTypedSet=[1,2,3,4.5,6,7.10.123]
+[myTypedSet].contentClass=java.lang.Double
+myTypedList=[CHF 10.20, EUR 12.20, BTC 0.002]
+[myTypedList].contentType=org.javamoney.moneta.FastMoney
+myTypedMap=[CHF:CHF 10.20, EUR:EUR 12.20, BTC:BTC 0.002]
+[myTypedMap].contentTypes=javax.money.CurrencyUnit,javax.money.MonetaryAmount
+----------------------------------------------------------------
+
+
+=== Management Service
+
+A JMX/Restful API should be designed and built that exposes configuration information. Access should be secured, e.g.
+using OAuth or other security mechasnisms.
+
+=== Management Client
+
+A nice web-based client to manage configuration data would be nice as well. This also includes a UI for creating new
+configurations.
+
+=== Mapping Configuration to a Database
+
+A flexible mechanism should be implemented that allows the use of databases (SQL/JPA as well as non-SQL) for
+storing/retreiving/managing configuration:
+
+* JPA, Hibernate
+* MongoDB
+* ...
+
+=== Integration with OSGI
+
+Examples are to be created and tested, where OSGI is used as the basic runtime platform, e.g. Apache Felix, but as well
+others.
+
+=== Integration with Jigsaw
+
+Once Jigsaw is mature and in a usable (still early) stage, examples are to be created and tested, where OSGI is used as
+the basic runtime platform, e.g. Apache Felix, but as well others.
+
+== Distributed/Remote Configuration Support
+
+=== Configuration Server
+
+A configuration server should be implemented that provides access to configurations and triggers updates to registered
+clients (push). Similarly a poull model should be supported, where clients can asl for the current version id of a certain
+configuration and reload it if necessary.
+
+=== Configuration Distribution Policies
+
+Different configuration distribution policies should be defined any implemented, e.g. distributed cache, restful services,
+web services, EJB/RMI calls, asynchronous queues, publish/subsribe models, ...
+
+=== Dynamic Service Lookup
+
+Configuration Servers and Clients should bea ble to locate each other in different ways:
+
+* with fixed configured IPs, or IP ranges
+* using a dynamic service location protocol like
+** SLP
+** Distributed Maps/Datagrids
+** Apache Zookeeper
+
+=== Configuration Client
+
+A subset of the API would be created that exposes only a well defined subset, of exactly one configuration targeted
+to a certain instance, VM or whatever. The client should be connectable to a server in different ways (see configuration
+distributiont policies).
+
+=== Preferences Support
+
+Write a +PreferencesFactory+ for +java.util.preferences+.
+
+== Third Party Integration
+
+=== Integration with Deltaspike Config
+
+Integration with Deltaspike Config should be implemented and discussed with Deltaspike guys.
+
+=== Integration with Spring
+
+A {name} module should be created that allows Spring to be used either as client or configuration provider.
+
+=== Integration with Jetty
+
+A {name} module should be created that allows a Jetty instance to be deployed and started that is (completely)
+configured based on configuration server.
+
+=== Integration with Tomcat
+
+A {name} module should be created that allows a Tomcat instance to be deployed and started that is (completely)
+configured based on configuration server.
+
+=== Configuration of Java EE
+
+In the Java EE area there would be several options:
+
+=== Configuration of Application Servers (administrative resources)
+
+It should be possible to start a application server instance remotely and configure all administrative resources and the
+deployments based on the configuration service, server to be considered maybe
+
+* Wildfly
+* IBM
+* Weblogic
+* Glassfish
+* Apache Geronimo
+
+==== Configuration of CDI
+
+Implement a CDI extension that controls CDI based on configuration:
+* Add beans
+* Remove (veto) beans
+* Add/remove interceptors
+* Add/remove decorators
+* Activate alternatives
+* ...
+
+==== Configuration of Bean Validation
+
+* Add configurable validators.
+* Configure bean validation based on configuration
+* ...
+
+=== JNDI Support
+
+Write a +JCA+ adapter to provide configuration data through JNDI.
+
+==== Configure JSF
+
+Use the JSF +XML Document+ event to completely configure JSF.
+
+==== Configure Web Services
+
+Provide a WebServiceProviderFactory that may be configured.
+
+==== Configure JPA
+
+Provide an implementation that allows configuration of persistence units. Talk with JPA EG people to see if we can
+get an SPI to hook in a stadardized way.
+
+==== Configure EJBs
+
+Provide an implementation that allows configuration of EJBs and MDBs:
+
+* Register beans
+* Unregister/disable beans
+* Intercept beans
+* Support Configuration Injection (in the worst case using a standard Interceptor, provide supporting artifacts to
+ help developers to achive this easily).
+* Talk with EE8 Umbrella EG (Bill Shanon, Linda DeMichels) on a feasible SPI for EE8, if possible join the EG.
+
+==== Configure ...
+
+Just think of any Java EE aspects that might be worth to be configured. If it can be done, e.g. by managing CDI managed
+resources, it might be easy. For others it is a good idea to discuss things with our matter of experts...
+
+== Special Goodies
+
+=== Maintenance Mode Servlet Filter
+
+Provide a servlet filter that is capable of switching to maintenance mode, based on configuration. Similarly also a forwarding
+servlet could be useful, wehere only request based on configuration are forwarded, other might be rejected or dropped
+as configured.
+
+=== Dynamic Camel Routes
+
+Provides dynamic (configurable) Camel routes, e.g. usable within ServiceMix or standalone.
+
+=== Dynamic CXF
+
+Provides dynamic (configurable) CXF adapters, e.g. usable within ServiceMix or standalone.
+
+=== Configurable Apache MQ
+
+Provides an implementation for configuring Apache MQ.
+
+=== Dynamic Logging
+
+Provide a dynamic solution, where logging systems are configured (handlers, log levels etc), e.g. for
+
+* slf4j
+* log4j
+* JUL
+
+Also interesting is a feature that allows to dynamically log on a more finer level depending on a concrete
+subject/user, session id or server or ...
+
+=== Dynamic ...
+
+Interested to see what other ideas are around. Let us know!
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java b/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java
new file mode 100644
index 0000000..25b503b
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java
@@ -0,0 +1,172 @@
+/*
+ * 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;
+
+import java.beans.PropertyChangeEvent;
+import java.util.*;
+
+/**
+ * Event that contains a set of changes that were applied or could be applied.
+ * This class is immutable and thread-safe. To create instances use
+ * {@link ConfigChangeSetBuilder}.
+ *
+ * Created by Anatole on 22.10.2014.
+ */
+public final class ConfigChangeSet {
+ /** The base property provider/configuration. */
+ private PropertyProvider propertyProvider;
+ /** The base version, usable for optimistic locking. */
+ private String baseVersion;
+ /** The recorded changes. */
+ private Map<String,PropertyChangeEvent> changes = new HashMap<>();
+
+ /**
+ * Constructor used by {@link ConfigChangeSetBuilder}.
+ * @param propertyProvider The base property provider/configuration, not null.
+ * @param baseVersion The base version, usable for optimistic locking.
+ * @param changes The recorded changes, not null.
+ */
+ ConfigChangeSet(PropertyProvider propertyProvider, String baseVersion, Collection<PropertyChangeEvent> changes) {
+ this.propertyProvider = Objects.requireNonNull(propertyProvider);
+ this.baseVersion = baseVersion;
+ changes.forEach((c) -> this.changes.put(c.getPropertyName(), c));
+ }
+
+ /**
+ * Get the underlying property provider/configuration.
+ * @return the underlying property provider/configuration, never null.
+ */
+ public PropertyProvider getPropertyProvider(){
+ return this.propertyProvider;
+ }
+
+ /**
+ * Get the base version, usable for optimistic locking.
+ * @return the base version.
+ */
+ public String getBaseVersion(){
+ return baseVersion;
+ }
+
+ /**
+ * Get the changes recorded.
+ * @return the recorded changes, never null.
+ */
+ public Collection<PropertyChangeEvent> getEvents(){
+ return Collections.unmodifiableCollection(this.changes.values());
+ }
+
+ /**
+ * Access the number of removed entries.
+ * @return the number of removed entries.
+ */
+ public int getRemovedSize() {
+ return (int) this.changes.values().stream().filter((e) -> e.getNewValue() == null).count();
+ }
+
+ /**
+ * Access the number of added entries.
+ * @return the number of added entries.
+ */
+ public int getAddedSize() {
+ return (int) this.changes.values().stream().filter((e) -> e.getOldValue() == null).count();
+ }
+
+ /**
+ * Access the number of updated entries.
+ * @return the number of updated entries.
+ */
+ public int getUpdatedSize() {
+ 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 containsKey(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();
+ }
+
+ /**
+ * Applies all changes to the given map instance.
+ * @param map the target map.never null.
+ */
+ public void applyChangesTo(Map<String, String> map) {
+ for(Map.Entry<String,PropertyChangeEvent> en: this.changes.entrySet()){
+ if(en.getValue().getNewValue() == null){
+ map.remove(en.getKey());
+ }
+ else{
+ map.put(en.getKey(), (String)en.getValue().getNewValue());
+ }
+ }
+ }
+
+
+ @Override
+ public String toString() {
+ return "ConfigChangeSet{" +
+ "properties=" + propertyProvider +
+ ", baseVersion=" + baseVersion +
+ ", changes=" + changes +
+ '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
new file mode 100644
index 0000000..0d30764
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
@@ -0,0 +1,323 @@
+/*
+ * 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;
+
+import java.beans.PropertyChangeEvent;
+import java.util.*;
+
+/**
+ * Models a set of changes to be applied to a configuration/property provider. Such a set can be applied
+ * to any {@link PropertyProvider} instance. If the provider is mutable it may check the
+ * version given and apply the changes to the provider/configuration, including triggering of regarding
+ * change events.
+ *
+ * Created by Anatole on 06.09.2014.
+ */
+public final class ConfigChangeSetBuilder {
+ /** The recorded changes. */
+ final SortedMap<String, PropertyChangeEvent> delta = new TreeMap<>();
+ /** The underlying configuration/provider. */
+ PropertyProvider source;
+ /** The base version, if any. Used for optimistic version checking. */
+ String baseVersion;
+
+ /**
+ * Constructor.
+ * @param source the underlying configuration/provider, not null.
+ * @param baseVersion the base version, used for optimistic version checking.
+ */
+ private ConfigChangeSetBuilder(PropertyProvider source, String baseVersion) {
+ Objects.requireNonNull(source);
+ this.source = source;
+ this.baseVersion= baseVersion;
+ }
+
+ /**
+ * Creates a new instance of this builder.
+ * @param source the underlying property provider/configuration, not null.
+ * @param baseVersion the base version to be used.
+ * @return the builder for chaining.
+ */
+ public static ConfigChangeSetBuilder of(PropertyProvider source, String baseVersion) {
+ return new ConfigChangeSetBuilder(source, baseVersion);
+ }
+
+ /**
+ * Creates a new instance of this builder.
+ * @param configuration the base configuration, not null.
+ * @return the builder for chaining.
+ */
+ public static ConfigChangeSetBuilder of(Configuration configuration) {
+ return new ConfigChangeSetBuilder(configuration, configuration.getVersion());
+ }
+
+ /**
+ * Add a change as a {@link java.beans.PropertyChangeEvent}.
+ * @param changeEvent the change event.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder addChange(PropertyChangeEvent changeEvent) {
+ Objects.requireNonNull(changeEvent);
+ this.delta.put(changeEvent.getPropertyName(), changeEvent);
+ 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 ConfigChangeSetBuilder addChanges(PropertyProvider newState) {
+ compare(newState, this.source).forEach((c) -> 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 of the entry, not null.
+ * @return the value, 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) from the configuration/properties to be removed.
+ * @param key the key of the entry, not null.
+ * @param otherKeys additional keys to be removed (convenience), not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder remove(String key, String... otherKeys) {
+ String oldValue = this.source.get(key).orElse(null);
+ 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).orElse(null);
+ if(oldValue==null){
+ this.delta.remove(addKey);
+ }
+ this.delta.put(addKey, new PropertyChangeEvent(this.source, addKey, oldValue, null));
+ }
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, String value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value)));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Boolean value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Byte value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Character value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Short value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Integer value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Long value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Float value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Double value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Applies the given value.
+ * @param key the key of the entry, not null.
+ * @param value the value to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder put(String key, Object value) {
+ this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString()));
+ return this;
+ }
+
+ /**
+ * Apply all the given values to the base configuration/properties.
+ * @param changes the changes to be applied, not null.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder putAll(Map<String,String> changes) {
+ changes.forEach((k,v) ->
+ this.delta.put(k, new PropertyChangeEvent(this.source, k, this.source.get(k).orElse(null), v)));
+ return this;
+ }
+
+ /**
+ * This method will create a change set that clears all entries from the given base configuration/properties.
+ * @return the builder for chaining.
+ */
+ public ConfigChangeSetBuilder clear() {
+ this.delta.clear();
+ this.source.toMap().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 set, 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 ConfigChangeSet build() {
+ return new ConfigChangeSet(this.source, baseVersion, Collections.unmodifiableCollection(this.delta.values()));
+ }
+
+ /**
+ * Compares the two property providers/configurations and creates a collection of 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 of change events, never null.
+ */
+ public static Collection<PropertyChangeEvent> compare(PropertyProvider map1, PropertyProvider map2) {
+ List<PropertyChangeEvent> changes = new ArrayList<>();
+ for (Map.Entry<String, String> en : map1.toMap().entrySet()) {
+ Optional<String> val = map2.get(en.getKey());
+ if(!val.isPresent()) {
+ changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue()));
+ }
+ else if(!val.get().equals(en.getValue())){
+ changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue()));
+ }
+ }
+ for (Map.Entry<String, String> en : map2.toMap().entrySet()) {
+ Optional<String> val = map1.get(en.getKey());
+ if(!val.isPresent()) {
+ changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue()));
+ }
+ else if(!val.equals(en.getValue())){
+ changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue()));
+ }
+ }
+ return changes;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "PropertyChangeEventBuilder [source=" + source + ", " +
+ ", delta=" + delta + "]";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigException.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigException.java b/api/src/main/java/org/apache/tamaya/ConfigException.java
new file mode 100644
index 0000000..bac2ef4
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigException.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * Exception class (runtime exception) for configuration issues.
+ */
+public class ConfigException extends RuntimeException{
+
+ private static final long serialVersionUID = -5886094818057522680L;
+
+ /**
+ * Creates a new configuration exception.
+ * @param message the exception message, not null.
+ */
+ public ConfigException(String message){
+ super(message);
+ }
+
+ /**
+ * Creates a new configuration exception.
+ * @param message the exception message, not null.
+ * @param t the throwable.
+ */
+ public ConfigException(String message, Throwable t){
+ super(message, t);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigOperator.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigOperator.java b/api/src/main/java/org/apache/tamaya/ConfigOperator.java
new file mode 100644
index 0000000..a59712b
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigOperator.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+
+/**
+ * Interface for an filter/operator that converts a configured String into another String. One typical
+ * use case would the the decryption of an encrypted configuration value.
+ */
+@FunctionalInterface
+public interface ConfigOperator{
+
+ /**
+ * Method that creates a Configuration from another Configuration. This can be used for implementing
+ * views, security constraints or for overriding/inheriting of configuration.
+ * @param config The target configuration to be operated, never nnull.
+ * @return the operated configuration, never null.
+ */
+ Configuration operate(Configuration config);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigQuery.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigQuery.java b/api/src/main/java/org/apache/tamaya/ConfigQuery.java
new file mode 100644
index 0000000..c296d12
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigQuery.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+
+/**
+ * Interface for an query that converts a Configuration into another object. One typical
+ * use cases would creating a complex configuration parameter type from a Configuration instance or
+ * constraint views on configuration.
+ */
+@FunctionalInterface
+public interface ConfigQuery<T>{
+
+ /**
+ * Queries the given configuration.
+ * @param config the configuration to be wuiried, not null.
+ * @return the result T.
+ */
+ T query(Configuration config);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/Configuration.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/Configuration.java b/api/src/main/java/org/apache/tamaya/Configuration.java
new file mode 100644
index 0000000..a23f44c
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/Configuration.java
@@ -0,0 +1,376 @@
+/*
+ * 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;
+
+import com.sun.javafx.scene.control.behavior.OptionalBoolean;
+
+import java.beans.PropertyChangeListener;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * A configuration models a aggregated set of properties, identified by a unique key, but adds higher level access functions to
+ * a {@link PropertyProvider}. Hereby in most cases a configuration is a wrapper around a composite
+ * {@link PropertyProvider} instance, which may combine multiple child providers in well defined tree like structure,
+ * where nodes define logically the rules of priority, filtering, combination and overriding.
+ * <br/>
+ * <h3>Implementation Requirements</h3>
+ * Implementations of this interface must be
+ * <ul>
+ * <li>Thread safe.
+ * <li>Immutable
+ * </ul>
+ * It is not recommended that implementations also are serializable, since the any configuration can be <i>freezed</i>
+ * by reading out its complete configuration map into a serializable and remotable structure. This helps significantly
+ * simplifying the development of this interface, e.g. for being backed up by systems and stores that are not part of
+ * this library at all.
+ */
+public interface Configuration extends PropertyProvider{
+
+ /**
+ * Get the property value as {@link Boolean}.
+ *
+ * @param key the property's absolute, or relative path, e.g. {@code
+ * a/b/c/d.myProperty}.
+ * @return the property's value.
+ */
+ default OptionalBoolean getBoolean(String key){
+ Optional<Boolean> val = get(key, Boolean.class);
+ if(val.isPresent()){
+ if(val.get()){
+ return OptionalBoolean.TRUE;
+ }
+ return OptionalBoolean.FALSE;
+ }
+ return OptionalBoolean.ANY;
+ }
+
+ /**
+ * Get the property value as {@link Integer}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @return the property's value.
+ */
+ default OptionalInt getInteger(String key){
+ Optional<Integer> val = get(key, Integer.class);
+ if(val.isPresent()){
+ return OptionalInt.of(val.get());
+ }
+ return OptionalInt.empty();
+ }
+
+
+ /**
+ * Get the property value as {@link Long}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @return the property's value.
+ */
+ default OptionalLong getLong(String key){
+ Optional<Long> val = get(key, Long.class);
+ if(val.isPresent()){
+ return OptionalLong.of(val.get());
+ }
+ return OptionalLong.empty();
+ }
+
+
+ /**
+ * Get the property value as {@link Double}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @return the property's value.
+ * @throws IllegalArgumentException if no such property exists.
+ */
+ default OptionalDouble getDouble(String key){
+
+ Optional<Double> val = get(key, Double.class);
+ if(val.isPresent()){
+ return OptionalDouble.empty().of(val.get());
+ }
+ return OptionalDouble.empty();
+ }
+
+
+ /**
+ * Get the property value as type {@code Class<T>}.
+ * <p>
+ * If {@code Class<T>} is not one of
+ * {@code Boolean, Short, Integer, Long, Float, Double, BigInteger,
+ * BigDecimal, String} , an according adapter must be
+ * available to perform the conversion from {@link String} to
+ * {@code Class<T>}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @param adapter the PropertyAdapter to perform the conversion from
+ * {@link String} to {@code Class<T>}, not {@code null}.
+ * @return the property's value.
+ * @throws IllegalArgumentException if the value could not be converted to the required target
+ * type, or no such property exists.
+ */
+ default <T> Optional<T> getAdapted(String key, PropertyAdapter<T> adapter){
+ Optional<String> value = get(key);
+ if(value.isPresent()) {
+ return Optional.ofNullable(adapter.adapt(value.get()));
+ }
+ return Optional.empty();
+ }
+
+
+ /**
+ * Get the property value as type T. This will implicitly require a corresponding {@link
+ * PropertyAdapter} to be available that is capable of providing type T
+ * from the given String value.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @param type The target type required, not null.
+ * @return the property's value.
+ * @throws IllegalArgumentException if the value could not be converted to the required target
+ * type.
+ */
+ <T> Optional<T> get(String key, Class<T> type);
+
+ /**
+ * Return a set with all fully qualifies area names.
+ *
+ * @return s set with all areas, never {@code null}.
+ */
+ default Set<String> getAreas(){
+ final Set<String> areas = new HashSet<>();
+ this.keySet().forEach(s -> {
+ int index = s.lastIndexOf('.');
+ if(index > 0){
+ areas.add(s.substring(0, index));
+ }
+ else{
+ areas.add("<root>");
+ }
+ });
+ return areas;
+ }
+
+ /**
+ * Return a set with all fully qualified area names, containing the transitive closure also including all
+ * subarea names, regardless if properties are accessible or not.
+ *
+ * @return s set with all transitive areas, never {@code null}.
+ */
+ default Set<String> getTransitiveAreas(){
+ final Set<String> transitiveAreas = new HashSet<>();
+ getAreas().forEach(s -> {
+ int index = s.lastIndexOf('.');
+ if (index < 0) {
+ transitiveAreas.add("<root>");
+ } else {
+ while (index > 0) {
+ s = s.substring(0, index);
+ transitiveAreas.add(s);
+ index = s.lastIndexOf('.');
+ }
+ }
+ });
+ return transitiveAreas;
+ }
+
+ /**
+ * Return a set with all fully qualified area names, containing only the
+ * areas that match the predicate and have properties attached
+ *
+ * @param predicate A predicate to deternine, which areas should be returned, not {@code null}.
+ * @return s set with all areas, never {@code null}.
+ */
+ default Set<String> getAreas(final Predicate<String> predicate){
+ return getAreas().stream().filter(predicate).collect(Collectors.toCollection(TreeSet::new));
+ }
+
+ /**
+ * Return a set with all fully qualified area names, containing the transitive closure also including all
+ * subarea names, regardless if properties are accessible or not.
+ *
+ * @param predicate A predicate to deternine, which areas should be returned, not {@code null}.
+ * @return s set with all transitive areas, never {@code null}.
+ */
+ default Set<String> getTransitiveAreas(Predicate<String> predicate){
+ return getTransitiveAreas().stream().filter(predicate).collect(Collectors.toCollection(TreeSet::new));
+ }
+
+ /**
+ * Allows to evaluate if an area exists.
+ *
+ * @param key the configuration area (sub)path.
+ * @return {@code true}, if such a node exists.
+ */
+ default boolean containsArea(String key){
+ return getAreas().contains(key);
+ }
+
+ /**
+ * Extension point for adjusting configuration.
+ *
+ * @param operator A configuration operator, e.g. a filter, or an adjuster
+ * combining configurations.
+ * @return the new adjusted configuration, never {@code null}.
+ */
+ default Configuration with(ConfigOperator operator){
+ return operator.operate(this);
+ }
+
+ /**
+ * Query some value from a configuration.
+ *
+ * @param query the query, never {@code null}.
+ * @return the result
+ */
+ default <T> T query(ConfigQuery<T> query){
+ return query.query(this);
+ }
+
+ /**
+ * Field that allows property providers to be versioned, meaning that each change on a provider requires this value
+ * to be incremented by one. This can be easily used to implement versioning (and optimistic locking)
+ * in distributed (remote) usage scenarios.
+ * @return the version of the current instance, or 'N/A'.
+ */
+ default String getVersion(){return "N/A";}
+
+ /**
+ * Add a ConfigChangeListener to this configuration instance.
+ * @param l the listener, not null.
+ */
+ void addPropertyChangeListener(PropertyChangeListener l);
+
+ /**
+ * Removes a ConfigChangeListener to this configuration instance.
+ * @param l the listener, not null.
+ */
+ void removePropertyChangeListener(PropertyChangeListener l);
+
+ /**
+ * Allows to check if a configuration with a given name is defined.
+ *
+ * @param name the configuration's name, not null, not empty.
+ * @return true, if such a configuration is defined.
+ */
+ public static boolean isDefined(String name){
+ return ConfigurationManager.isConfigurationDefined(name);
+ }
+
+ /**
+ * Access a configuration by name.
+ *
+ * @param name the configuration's name, not null, not empty.
+ * @param template the annotated configuration's
+ * template interface, not null.
+ * @return the corresponding Configuration instance, never null.
+ * @throws ConfigException if no such configuration is defined.
+ */
+ public static <T> T of(String name, Class<T> template){
+ return ConfigurationManager.getConfiguration(name, template);
+ }
+
+
+ /**
+ * Access a configuration by name.
+ *
+ * @param name the configuration's name, not null, not empty.
+ * @return the corresponding Configuration instance, never null.
+ * @throws ConfigException if no such configuration is defined.
+ */
+ public static Configuration of(String name){
+ return ConfigurationManager.getConfiguration(name);
+ }
+
+ /**
+ * Access a configuration.
+ *
+ * @return the corresponding Configuration instance, never null.
+ * @throws ConfigException if no such configuration is defined.
+ */
+ public static Configuration of(){
+ return ConfigurationManager.getConfiguration();
+ }
+
+ /**
+ * Access a typed configuration, based on the default configuration.
+ *
+ * @param type the annotated configuration type (could be an interface or
+ * a non abstract class), not null.
+ * @return the corresponding typed Configuration instance, never null.
+ * @throws ConfigException if the configuration could not be resolved.
+ */
+ public static <T> T of(Class<T> type){
+ return ConfigurationManager.getConfiguration(type);
+ }
+
+ /**
+ * Configures an instance, by resolving and injecting the configuration
+ * entries.
+ *
+ * @param instance the instance with configuration annotations, not null.
+ * @return the corresponding typed Configuration instance, never null.
+ * @throws ConfigException if the configuration could not be resolved.
+ */
+ public static void configure(Object instance){
+ ConfigurationManager.configure(instance);
+ }
+
+ /**
+ * Evaluate the current expression based on the current configuration valid.
+ *
+ * @param expression the expression, not null.
+ * @return the evaluated config expression.
+ */
+ public static String evaluateValue(String expression){
+ return ConfigurationManager.evaluateValue(expression);
+ }
+
+ /**
+ * Evaluate the current expression based on the current configuration valid.
+ *
+ * @param config The configuration to be used for evluating, not null.
+ * @param expression the expression, not null.
+ * @return the evaluated config expression.
+ */
+ public static String evaluateValue(Configuration config, String expression){
+ return ConfigurationManager.evaluateValue(config, expression);
+ }
+
+ /**
+ * Adds a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes.
+ * @param listener the {@link java.beans.PropertyChangeListener} instance to be added, not null.
+ */
+ public static void addGlobalPropertyChangeListener(PropertyChangeListener listener){
+ ConfigurationManager.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Removes a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes,
+ * if one is currently registered.
+ * @param listener the {@link java.beans.PropertyChangeListener} instance to be removed, not null.
+ */
+ public static void removeGlobalPropertyChangeListener(PropertyChangeListener listener){
+ ConfigurationManager.removePropertyChangeListener(listener);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigurationManager.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigurationManager.java b/api/src/main/java/org/apache/tamaya/ConfigurationManager.java
new file mode 100644
index 0000000..67d4ccb
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigurationManager.java
@@ -0,0 +1,161 @@
+/*
+ * 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;
+
+import org.apache.tamaya.spi.Bootstrap;
+import org.apache.tamaya.spi.ConfigurationManagerSingletonSpi;
+
+import java.beans.PropertyChangeListener;
+import java.util.Optional;
+
+/**
+ * Singleton accessor for accessing {@link Configuration} instances and
+ * proxied configuration templates.
+ */
+final class ConfigurationManager{
+ /**
+ * The backing SPI instance.
+ */
+ private static final ConfigurationManagerSingletonSpi configManagerSingletonSpi = loadConfigServiceSingletonSpi();
+
+ /**
+ * Private singleton constructor.
+ */
+ private ConfigurationManager(){
+ }
+
+ /**
+ * Method to initially load the singleton SPI from the {@link org.apache.tamaya.spi.Bootstrap} mechanism.
+ * The instance loaded will be used until the VM is shutdown. In case use cases require more flexibility
+ * it should be transparently implemented in the SPI implementation. This singleton will simply delegate calls
+ * and not cache any responses.
+ *
+ * @return the SPI, never null.
+ */
+ private static ConfigurationManagerSingletonSpi loadConfigServiceSingletonSpi(){
+ return Bootstrap.getService(ConfigurationManagerSingletonSpi.class);
+ }
+
+ /**
+ * Allows to check if a configuration with a given name is defined.
+ *
+ * @param name the configuration's name, not null, not empty.
+ * @return true, if such a configuration is defined.
+ */
+ public static boolean isConfigurationDefined(String name){
+ return Optional.of(configManagerSingletonSpi).get().isConfigurationDefined(name);
+ }
+
+ /**
+ * Access a configuration by name.
+ *
+ * @param name the configuration's name, not null, not empty.
+ * @param template the annotated configuration's
+ * template interface, not null.
+ * @return the corresponding Configuration instance, never null.
+ * @throws ConfigException if no such configuration is defined.
+ */
+ public static <T> T getConfiguration(String name, Class<T> template){
+ return Optional.of(configManagerSingletonSpi).get().getConfiguration(name, template);
+ }
+
+
+ /**
+ * Access a configuration by name.
+ *
+ * @param name the configuration's name, not null, not empty.
+ * @return the corresponding Configuration instance, never null.
+ * @throws ConfigException if no such configuration is defined.
+ */
+ public static Configuration getConfiguration(String name){
+ return Optional.of(configManagerSingletonSpi).get().getConfiguration(name);
+ }
+
+ /**
+ * Access a configuration.
+ *
+ * @return the corresponding Configuration instance, never null.
+ * @throws ConfigException if no such configuration is defined.
+ */
+ public static Configuration getConfiguration(){
+ return Optional.of(configManagerSingletonSpi).get().getConfiguration();
+ }
+
+ /**
+ * Access a typed configuration, based on the default configuration.
+ *
+ * @param type the annotated configuration type (could be an interface or
+ * a non abstract class), not null.
+ * @return the corresponding typed Configuration instance, never null.
+ * @throws ConfigException if the configuration could not be resolved.
+ */
+ public static <T> T getConfiguration(Class<T> type){
+ return Optional.of(configManagerSingletonSpi).get().getConfiguration(type);
+ }
+
+ /**
+ * Configures an instance, by resolving and injecting the configuration
+ * entries.
+ *
+ * @param instance the instance with configuration annotations, not null.
+ * @return the corresponding typed Configuration instance, never null.
+ * @throws ConfigException if the configuration could not be resolved.
+ */
+ public static void configure(Object instance){
+ Optional.of(configManagerSingletonSpi).get().configure(instance);
+ }
+
+ /**
+ * Evaluate the current expression based on the current configuration valid.
+ *
+ * @param expression the expression, not null.
+ * @return the evaluated config expression.
+ */
+ public static String evaluateValue(String expression){
+ return evaluateValue(getConfiguration(), expression);
+ }
+
+ /**
+ * Evaluate the current expression based on the current configuration valid.
+ *
+ * @param config The configuration to be used for evluating, not null.
+ * @param expression the expression, not null.
+ * @return the evaluated config expression.
+ */
+ public static String evaluateValue(Configuration config, String expression){
+ return Optional.of(configManagerSingletonSpi).get().evaluateValue(config, expression);
+ }
+
+ /**
+ * Adds a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes.
+ * @param listener the {@link java.beans.PropertyChangeListener} instance to be added, not null.
+ */
+ public static void addPropertyChangeListener(PropertyChangeListener listener){
+ Optional.of(configManagerSingletonSpi).get().addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Removes a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes,
+ * if one is currently registered.
+ * @param listener the {@link java.beans.PropertyChangeListener} instance to be removed, not null.
+ */
+ public static void removePropertyChangeListener(PropertyChangeListener listener){
+ Optional.of(configManagerSingletonSpi).get().removePropertyChangeListener(listener);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/Environment.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/Environment.java b/api/src/main/java/org/apache/tamaya/Environment.java
new file mode 100644
index 0000000..3523083
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/Environment.java
@@ -0,0 +1,167 @@
+/*
+ * 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;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Models a runtime environment. Instances of this class are used to
+ * evaluate the correct configuration artifacts.<br/>
+ * <h3>Implementation Requirements</h3>
+ * <p>
+ * Implementations of this interface must be
+ * <ul>
+ * <li>Thread safe.
+ * <li>Immutable
+ * <li>serializable
+ * </ul>
+ */
+public interface Environment extends StageSupplier, Iterable<Environment>{
+
+ /**
+ * Get a unique type (within this VM) for this environment.
+ * Types represent the environment level within the hierarchy
+ * of possible environments, e.g. {@code system, ear, webapp, tenant}.
+ */
+ String getEnvironmentType();
+
+ /**
+ * Get a unique name (in combination with the environment type within this VM)
+ * for this environment instance.
+ * Where a human readable name is available this would be preferable
+ * over a technical key/UUID.
+ * @return a unique id for this environment, when comined with the environment type.
+ */
+ String getEnvironmentId();
+
+ /**
+ * Access a property.
+ * @param key the property's key, not null.
+ * @return the property's value.
+ */
+ Optional<String> get(String key);
+
+ /**
+ * Checks if a property is defined.
+ * @param key the property's key, not null.
+ * @return true, if the property is existing.
+ */
+ boolean containsKey(String key);
+
+ /**
+ * Access a property.
+ * @param key the property's key, not null.
+ * @return the property's value.
+ */
+ default String getOrDefault(String key, String defaultValue){
+ return get(key).orElse(defaultValue);
+ }
+
+ /**
+ * Access the set of property keys, defined by this provider.
+ * @return the key set, never null.
+ */
+ Set<String> keySet();
+
+ /**
+ * Get an qualified path to this environment instance, by appending the
+ * current environment id and type (in backets) with the ones of its parent and so on, e.g.
+ * <code>root[system].HumanOne[ear].rest[webapp].atsticks[user]</code>
+ * @return the qualified path of this environment instance
+ */
+ String getContext();
+
+ /**
+ * Get the parent context.
+ * @return the parent context, or null.
+ */
+ Environment getParentEnvironment();
+
+ /**
+ * Access the environment as Map.
+ * @return the Map instance containing the environments properties, never null.
+ */
+ Map<String,String> toMap();
+
+ /**
+ * Get the current {@link org.apache.tamaya.Environment}. The environment is used to determine the current runtime state, which
+ * is important for returning the correct configuration.
+ * @return the current Environment, never null.
+ */
+ public static Environment of(){
+ return EnvironmentManager.getEnvironment();
+ }
+
+ /**
+ * Get the current root (startup/machine/VM) {@link org.apache.tamaya.Environment}.
+ * @return the current root Environment, never null.
+ */
+ public static Environment getRootEnvironment(){
+ return EnvironmentManager.getRootEnvironment();
+ }
+
+ /**
+ * Evaluate the overall chain of possible environments.
+ * @return the hierarchy chain of possible Environments.
+ */
+ public static List<String> getEnvironmentTypeOrder(){
+ return EnvironmentManager.getEnvironmentTypeOrder();
+ }
+
+ /**
+ * Evaluate the current type chain of environments.
+ * @return the current type chain of Environments.
+ */
+ public static List<String> getEnvironmentHierarchy(){
+ return EnvironmentManager.getEnvironmentHierarchy();
+ }
+
+ /**
+ * Get a environment of the given environment type and context.
+ * @param environmentType the target type, not null.
+ * @param contextId the target context, not null.
+ * @return the corresponding environment, if available.
+ */
+ public static Optional<Environment> getEnvironment(String environmentType, String contextId){
+ return EnvironmentManager.getEnvironment(environmentType, contextId);
+ }
+
+ /**
+ * Get the currently known environment contexts of a given environment type.
+ * @param environmentType the target environment type.
+ * @return the corresponding environment contexts known, never null.
+ */
+ public static Set<String> getEnvironmentContexts(String environmentType){
+ return EnvironmentManager.getEnvironmentContexts(environmentType);
+ }
+
+ /**
+ * Allows to check, if the czurrent environment type is one of the current active environment types.
+ * @param environmentType the environment type to be queried.
+ * @return true, if the czurrent environment type is one of the current active environment types.
+ */
+ public static boolean isEnvironmentActive(String environmentType){
+ return EnvironmentManager.isEnvironmentActive(environmentType);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/EnvironmentManager.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/EnvironmentManager.java b/api/src/main/java/org/apache/tamaya/EnvironmentManager.java
new file mode 100644
index 0000000..7de86a6
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/EnvironmentManager.java
@@ -0,0 +1,136 @@
+/*
+ * 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;
+
+import org.apache.tamaya.spi.Bootstrap;
+import org.apache.tamaya.spi.EnvironmentManagerSingletonSpi;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+
+/**
+ * Singleton accessor class for the current environment.
+ */
+final class EnvironmentManager{
+
+ private static final EnvironmentManagerSingletonSpi environmentManagerSingletonSpi = loadContextProviderSpi();
+
+ /**
+ * Private singleton constructor.
+ */
+ private EnvironmentManager(){}
+
+ /**
+ * Method to load the environment SPI during initial load.
+ * @return the EnvironmentProviderSpi SPI, never null.
+ */
+ private static EnvironmentManagerSingletonSpi loadContextProviderSpi(){
+ return Bootstrap.getService(EnvironmentManagerSingletonSpi.class);
+ }
+
+ /**
+ * Get the current {@link Environment}. The environment is used to determine the current runtime state, which
+ * is important for returning the correct configuration.
+ * @return the current Environment, never null.
+ */
+ public static Environment getEnvironment(){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).getEnvironment();
+ }
+
+ /**
+ * Get the current root (startup/machine/VM) {@link Environment}.
+ * @return the current root Environment, never null.
+ */
+ public static Environment getRootEnvironment(){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).getRootEnvironment();
+ }
+
+ /**
+ * Evaluate the overall chain of possible environments.
+ * @return the hierarchy chain of possible Environments.
+ */
+ public static List<String> getEnvironmentTypeOrder(){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).getEnvironmentTypeOrder();
+ }
+
+ /**
+ * Evaluate the current type chain of environments.
+ * @return the current type chain of Environments.
+ */
+ public static List<String> getEnvironmentHierarchy(){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).getEnvironmentHierarchy();
+ }
+
+ /**
+ * Get the current environment of the given environment type.
+ * @param environmentType the target type.
+ * @return the corresponding environment
+ * @throws IllegalArgumentException if not such type is present or active.
+ */
+ public static Optional<Environment> getEnvironment(String environmentType){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).getEnvironment(environmentType);
+ }
+
+ /**
+ * Get a environment of the given environment type and context.
+ * @param environmentType the target type, not null.
+ * @param contextId the target context, not null.
+ * @return the corresponding environment, if available.
+ */
+ public static Optional<Environment> getEnvironment(String environmentType, String contextId){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).getEnvironment(environmentType, contextId);
+ }
+
+ /**
+ * Get the currently known environment contexts of a given environment type.
+ * @param environmentType the target environment type.
+ * @return the corresponding environment contexts known, never null.
+ */
+ public static Set<String> getEnvironmentContexts(String environmentType){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).getEnvironmentContexts(environmentType);
+ }
+
+ /**
+ * Allows to check, if the czurrent environment type is one of the current active environment types.
+ * @param environmentType the environment type to be queried.
+ * @return true, if the czurrent environment type is one of the current active environment types.
+ */
+ public static boolean isEnvironmentActive(String environmentType){
+ return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow(
+ () -> new IllegalStateException("No SPI loaded.")
+ ).isEnvironmentActive(environmentType);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/MetaInfo.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/MetaInfo.java b/api/src/main/java/org/apache/tamaya/MetaInfo.java
new file mode 100644
index 0000000..8be9122
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/MetaInfo.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;
+
+import java.util.*;
+
+/**
+ * Simple class to represent configuration meta information. Metainformation can be related to the holw
+ * configuration or some if its entries.
+ */
+public final class MetaInfo{
+
+ private final Map<String, String> metaInfo = new HashMap<>();
+
+ MetaInfo(MetaInfoBuilder builder){
+ Objects.requireNonNull(builder);
+ this.metaInfo.putAll(builder.map);
+ }
+
+ public static MetaInfo of(String info){
+ return MetaInfoBuilder.of(info).build();
+ }
+
+ public String get(String key){
+ return this.metaInfo.get(key);
+
+ }
+ public Set<String> keySet(){
+ return this.metaInfo.keySet();
+ }
+
+ public Map<? extends String,? extends String> toMap(){
+ return Collections.unmodifiableMap(this.metaInfo);
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder b = new StringBuilder("MetaInfo[");
+ for(Map.Entry<String,String> en:metaInfo.entrySet()){
+ b.append(escape(en.getKey())).append('=').append(escape(en.getValue())).append(", ");
+ }
+ if(!metaInfo.isEmpty()){
+ b.setLength(b.length()-2);
+ }
+ b.append(']');
+ return b.toString();
+ }
+
+ static String escape(String val){
+ return val.replaceAll("=", "\\\\=").replaceAll("\\[", "\\\\[").replaceAll("]", "\\\\]").replaceAll("\\,", "\\\\,");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java b/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java
new file mode 100644
index 0000000..7c527bc
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java
@@ -0,0 +1,162 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Builder class to create new instances of {@lin MetaInfo}.
+ */
+public final class MetaInfoBuilder{
+
+ public static final String METAINFO = "_metainfo";
+ public static final String TIMESTAMP = "timestamp";
+ public static final String CONTEXT = "context";
+ public static final String NAME = "name";
+ public static final String INFO = "info";
+ public static final String TYPE = "type";
+ public static final String SOURCE = "source";
+ public static final String ENVIRONMENT = "environment";
+ public static final String SOURCE_EXPRESSION = "source-expression";
+
+ Map<String,String> map = new ConcurrentHashMap<>();
+
+ private MetaInfoBuilder(MetaInfo metaInfo){
+ if(metaInfo!=null){
+ this.map.putAll(metaInfo.toMap());
+ }
+ }
+
+ public static MetaInfoBuilder of(MetaInfo metaInfo){
+ return new MetaInfoBuilder(metaInfo);
+ }
+
+ public static MetaInfoBuilder of(String info){
+ return new MetaInfoBuilder(null).setInfo(info);
+ }
+
+ public static MetaInfoBuilder of(){
+ return new MetaInfoBuilder(null);
+ }
+
+ public MetaInfoBuilder withName(String name){
+ Objects.requireNonNull(name);
+ map.put(NAME, name);
+ return this;
+ }
+
+ public MetaInfoBuilder setName(String name){
+ Objects.requireNonNull(name);
+ map.put(NAME, name);
+ return this;
+ }
+
+ public MetaInfoBuilder setType(String type){
+ Objects.requireNonNull(type);
+ map.put(TYPE, type);
+ return this;
+ }
+
+ public MetaInfoBuilder setInfo(String info){
+ Objects.requireNonNull(info);
+ map.put(INFO, info);
+ return this;
+ }
+
+ public MetaInfoBuilder setSources(String... sources){
+ Objects.requireNonNull(sources);
+ map.put(SOURCE, Arrays.toString(sources));
+ return this;
+ }
+
+ public MetaInfoBuilder setMetaInfo(String key, String metaInfo){
+ Objects.requireNonNull(metaInfo);
+ Objects.requireNonNull(key);
+ map.put(key + '.' + METAINFO, metaInfo);
+ return this;
+ }
+
+ public MetaInfoBuilder setMetaInfo(String metaInfo){
+ if(metaInfo!=null){
+ Objects.requireNonNull(metaInfo);
+ map.put(METAINFO, metaInfo);
+ }
+ return this;
+ }
+
+ public MetaInfoBuilder setMetaInfo(MetaInfo metaInfo){
+ if(metaInfo!=null){
+ Objects.requireNonNull(metaInfo);
+ map.putAll(metaInfo.toMap());
+ }
+ return this;
+ }
+
+ public MetaInfoBuilder setSourceExpressions(String... sourceExpressions){
+ Objects.requireNonNull(sourceExpressions);
+ map.put(SOURCE_EXPRESSION, Arrays.toString(sourceExpressions));
+ return this;
+ }
+
+ public MetaInfoBuilder setTimestamp(long timestamp){
+ map.put(TIMESTAMP, String.valueOf(timestamp));
+ return this;
+ }
+
+ public MetaInfoBuilder setContext(String context){
+ Objects.requireNonNull(context);
+ map.put(CONTEXT, context);
+ return this;
+ }
+
+ public MetaInfoBuilder setEnvironment(Environment configurationContext){
+ Objects.requireNonNull(configurationContext);
+ map.put(ENVIRONMENT, configurationContext.toString());
+ return this;
+ }
+
+ public MetaInfoBuilder set(String key, String value){
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(value);
+ map.put(key, value);
+ return this;
+ }
+
+ public MetaInfo build(){
+ return new MetaInfo(this);
+ }
+
+ @Override
+ public String toString(){
+ StringBuilder b = new StringBuilder("MetaInfoBuilder[");
+ for(Map.Entry<String,String> en:map.entrySet()){
+ b.append(MetaInfo.escape(en.getKey())).append('=').append(MetaInfo.escape(en.getValue())).append(", ");
+ }
+ if(!map.isEmpty()){
+ b.setLength(b.length()-2);
+ }
+ b.append(']');
+ return b.toString();
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/PropertyAdapter.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertyAdapter.java b/api/src/main/java/org/apache/tamaya/PropertyAdapter.java
new file mode 100644
index 0000000..6666907
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/PropertyAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+
+/**
+ * Interface for an adapter that converts a configured String into something else.
+ */
+public interface PropertyAdapter<T>{
+
+ /**
+ * Adapt the given configuration value to the required target type.
+ * @param value
+ * @return
+ */
+ T adapt(String value);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/PropertyAdapters.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertyAdapters.java b/api/src/main/java/org/apache/tamaya/PropertyAdapters.java
new file mode 100644
index 0000000..2103f60
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/PropertyAdapters.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+import org.apache.tamaya.annot.WithPropertyAdapter;
+import org.apache.tamaya.spi.Bootstrap;
+import org.apache.tamaya.spi.PropertyAdaptersSingletonSpi;
+import java.util.Optional;
+
+/**
+ * Singleton accessor that provides {@link PropertyAdapter} instance, usable for converting String
+ * based configuration entries into any other target types.
+ */
+public final class PropertyAdapters{
+ /** The SPI finally registered backing this singleton. */
+ private static final PropertyAdaptersSingletonSpi propertyAdaptersSingletonSpi = loadConfigAdapterProviderSpi();
+
+ /**
+ * Method that loads the singleton backing bean from the {@link org.apache.tamaya.spi.Bootstrap} component.
+ * @return the PropertyAdaptersSingletonSpi, never null.
+ */
+ private static PropertyAdaptersSingletonSpi loadConfigAdapterProviderSpi(){
+ return Bootstrap.getService(PropertyAdaptersSingletonSpi.class);
+ }
+
+ /**
+ * Orivate singleton constructor.
+ */
+ private PropertyAdapters(){}
+
+ /**
+ * Registers a new PropertyAdapter for the given target type, hereby replacing any existing adapter for
+ * this type.
+ * @param targetType The target class, not null.
+ * @param adapter The adapter, not null.
+ * @param <T> The target type
+ * @return any adapter replaced with the new adapter, or null.
+ */
+ public static <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> adapter){
+ return Optional.of(propertyAdaptersSingletonSpi).get().register(targetType, adapter);
+ }
+
+ /**
+ * Get an adapter converting to the given target type.
+ * @param targetType the target type class
+ * @return true, if the given target type is supported.
+ */
+ public static boolean isTargetTypeSupported(Class<?> targetType){
+ return Optional.of(propertyAdaptersSingletonSpi).get().isTargetTypeSupported(targetType);
+ }
+
+ /**
+ * Get an adapter converting to the given target type.
+ * @param targetType the target type class
+ * @param <T> the target type
+ * @return the corresponding adapter, never null.
+ * @throws ConfigException if the target type is not supported.
+ */
+ public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType){
+ return getAdapter(targetType, null);
+ }
+
+ /**
+ * Get an adapter converting to the given target type.
+ * @param targetType the target type class
+ * @param annotation the {@link org.apache.tamaya.annot.WithPropertyAdapter} annotation, or null. If the annotation is not null and
+ * defines an overriding adapter, this instance is created and returned.
+ * @param <T> the target type
+ * @return the corresponding adapter, never null.
+ * @throws ConfigException if the target type is not supported, or the overriding adapter cannot be
+ * instantiated.
+ */
+ public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType, WithPropertyAdapter annotation){
+ return Optional.of(propertyAdaptersSingletonSpi).get().getAdapter(targetType, annotation);
+ }
+
+}