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/12/26 01:56:08 UTC
[1/4] incubator-tamaya git commit: TAMAYA-19: Streamlined API and
impl.
Repository: incubator-tamaya
Updated Branches:
refs/heads/master 0b5d4feef -> a60570e8e
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/design/4_Extensions.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/design/4_Extensions.adoc b/docs/src/main/asciidoc/design/4_Extensions.adoc
new file mode 100644
index 0000000..db949ac
--- /dev/null
+++ b/docs/src/main/asciidoc/design/4_Extensions.adoc
@@ -0,0 +1,841 @@
+// 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.
+<<<
+[[CoreConcepts]]
+== {name} Core Concepts
+Though {name} is a very powerful and flexible solution there are basically only a few simple core concepts required that build
+the base of all the other mechanisms:
+
+The API contains the following core concepts/artifacts:
+
+* Literal Key/Value Pairs
+* _PropertyProvider:_ is the the SPI for a source that provides configuration data. A +PropertyProvider+
+ hereby defines
+ ** a minimalistic SPI to be implemented by the config data source
+ ** provides data key/value pairs in raw format as String key/values only
+ ** providers should not have any dependencies other than to the datasource
+ ** providers may read context dependent data, but basically providers themselves are not contextual.
+ Context management should be done by the ConfigurationProvider implementation that also is responsible
+ for combining a set of property providers to a Configuration.
+ _Configuration_ is the API that users of Tamaya will see, when they access configuration in raw format. Hereby +Configuration+
+ ** adds type support for non String types
+ ** provides functional extension points (+with,query+)
+ ** allows registering/deregistering of change listeners
+ ** is the entry point for evaluating the current +Configuration+
+ ** each +PropertyProvider+ can be easily converted into a +Configuration+
+ ** allows configuration entries to be injected
+ ** to access configuration _templates_ (annotated interfaces).
+ ** Configuration may support mutability by allowing instances of +ConfigChangeSet+ to be passed.
+* _PropertyProviders_ allows to aggregate different property providers. Hereby property providers are
+ seen as sets, which can be combined to new providers using set styled operations (aggregate, intersect, subtract).
+ This allows to model and create composite container providers, to build up more complex configuration models
+ step by step.
+* _MetaInfo_ is provided by each +Configuration, PropertyProvider+ and describes the configuration/provider and its entries.
+* _Environment_ is the base model for modelling the environment and the accessor for getting the current +Environment+ instance.
+* _Annotations_ a set of annotations allows to configure configuration injection on classes or interface (aka config templates).
+
+The SPI contains the following core concepts/artifacts:
+
+* _Bootstrap_ is the delegate singleton that is used by the framework to resolve components. The effective component
+ loading can be accessed by implementing and registering an instance of +ServiceProvider+ using +java.util.ServiceLoader+.
+* All the singleton used explicitly (+PropertyAdapters,PropertyProviders+ are backed up corresponding API interfaces.
+ To override a singleton's behaviour the corresponding SPI has to be implemented and registered, so it can be loaded
+ by the current +Bootstrap+ setup (by default ServiceLoader based).
+* Also the singleton used implicitly by +Configuration, Environment, Stage+ are backed up corresponding SPI interfaces.
+ To override a singleton's behaviour the corresponding SPI has to be implemented and registered, so it can be loaded
+ by the current +Bootstrap+ setup (by default ServiceLoader based).
+
+This is also reflected in the main parts of the API, which is quite small:
+
+* +org.apache.tamaya+ contains the main abstractions +Configuration, ConfigOperator, ConfigQuery, PropertyAdapter, Stage,
+ Environment, PropertyProvider, MetaInfo+
+* +org.apache.tamaya.spi+ contains the SPI interfaces to be implemented by implementations and the +Bootstrap+ mechanism.
++ +org.apache.tamaya.annot+ contains the annotations defined.
+
+In the implementation are there are additional projects:
+
+* +org.apache.tamaya.core+ contains the core implementation of the API. Deploying it together with the API results in a
+ flexible framework that can be easily used for configuration of different complexity. But out of the box this framework
+ will not do much more than exposing system and environment properties, its power comes when an additional meta-model
+ is defined and deployed. Hereby you can write your own, or use on e of the provided ones (see later).
+* the core part is extended by multiple additional modules
+ ** CDI integration
+ ** Default configuration meta-models and providers for the most common usage scenarios
+ *** standalone applications
+ *** Java EE
+ *** ...
+
+These parts are explained in the following sections. It is recommended that user's of the API read through this part.
+All subsequent parts are building upon this concepts and may be more difficult to understand without having read
+this section.
+
+
+[[APIKeyValues]]
+=== Key/Value Pairs
+
+Basically configuration is a very generic concept. Therefore it should be modelled in a generic way. The most simple
+and similarly most commonly used are simple literal key/value pairs. So the core building block of {name} are key/value pairs.
+You can think of a common +.properties+ file, e.g.
+
+[source,properties]
+.A simple properties file
+--------------------------------------------
+a.b.c=cVal
+a.b.c.1=cVal1
+a.b.c.2=cVal2
+a=aVal
+a.b=abVal
+a.b2=abVal
+--------------------------------------------
+
+Now you can use +java.util.Properties+ to read this file and access the corresponding properties, e.g.
+
+[source,properties]
+.Accessing some properties
+--------------------------------------------
+Properties props = new Properties();
+props.readProperties(...);
+String val = props.getProperty("a.b.c");
+val = props.getProperty("a.b.c.1");
+...
+--------------------------------------------
+
+This looks familiar to most of you. Nevertheless when looking closer to the above key/value pairs,
+there are more concepts in place: looking at the keys +a.b.c+, +a.b.c.1+, +a.b.c.2+, +a+, +a.b+ we
+see that the key names build up a flattened tree structure. So we can define the following:
+
+Given a key +p1.p2.p3.k=value+:
+
+* +p1.p2.p3.k+ is called the _qualified key_
+* +p1.p2.p3+ is the key's _area_
+* the child areas +p1.p2", "p1+ are called _areas_ as well
+* +k+ is the _(unqualified) key_
+
+Given that you can perform some very useful actions:
+
+* you can filter the keys with an area. E.g. in the example before you can query for all keys within the area +a.b.c+
+ and map them to new properties set as follows:
+
+[source,properties]
+.Accessing an area
+--------------------------------------------
+1=cVal1
+2=cVal2
+--------------------------------------------
+
+Similarly accessing the area +a+ results in the following properties:
+
+[source,properties]
+.Accessing the area +a+
+--------------------------------------------
+b=abVal
+b2=abVal
+--------------------------------------------
+
+Additionally you can access all values of an area recursively, so accessing +a+ recursively results in
+the following properties:
+
+[source,properties]
+.Accessing area +a+ recursively
+--------------------------------------------
+b.c=cVal
+b.c.1=cVal1
+b.c.2=cVal2
+b=abVal
+b2=abVal
+--------------------------------------------
+
+Why this is useful? Well there are different use cases:
+
+* you can segregate your configuration properties, e.g. a module can access its module configuration by
+ querying all properties under the area +config.modules.myModule+ (or whatever would be appropriate).
+* you can use this mechanism to configure maps (or more generally: collections).
+* you can easily filter parts of configuration
+* ...and more.
+
+==== Why Using Strings Only
+
+Using Strings as base representation of configuration comes with several huge advantages:
+
+* Strings are simple to understand
+* Strings are human readable and therefore easy to prove for correctness
+* Strings can easily be used within different language, different VMs, files or network communications.
+* Strings can easily be compared and manipulated
+* Strings can easily be searched, indexed and cached
+* It is very easy to provide Strings as configuration, which gives much flexibility for providing configuration in
+ production as well in testing.
+* and more
+
+On the other side there are also disadvantages:
+
+* Strings are inherently not type safe, they do not provide validation out of the box for special types, such as
+numbers,
+ dates etc.
+* Often you want not to work with Strings, but with according types.
+* Strings are not hierarchical, so mapping hierarchical structures requires some extra efforts.
+
+Nevertheless most of these advantages can be mitigated easily, hereby still keeping all the benefits from above:
+
+* Adding type safe converters on top of String allow to add any type easily, that can be directly mapped out of Strings.
+ This includes all common base types such as numbers, dates, time, but also timezones, formatting patterns and more.
+* Even more complex mappings can be easily realized, by using String not as a direct representation of configuration,
+ but a reference that defines where the more complex configuration artifact is available. This mechanism is similarly
+ easy to understand as parsing Strings to numbers, but is powerful enough to provide e.g. all kind of deployment
+ descriptors in Java EE.
+* Hierarchical and collection types can be mapped in different ways:
+** The keys of configuration can have additional syntax/semantics. E.g. when adding dor-separating path semantics
+*** trees/maps can also simply be mapped.
+
+[APIPropertyProviders]
+=== Property Providers
+==== Basic Model
+
+We have seen that constrain configuration aspects to simple literal key/value pairs provides us with an easy to
+understand, generic, flexible, yet expendable mechanism. Looking at the Java language features a +vava.util.Map<String,
+String>+ and +java.util.Properties+ basically model these quite well out of the box.
+So it makes sense to build configuration on top of the JDK's +Map+ interface. This creates immediately additional
+benefits:
+
+* we inherit full Lambda and collection support
+* Maps are widely known and well understood
+
+Nevertheless there are some things to be considered:
+
+* Configuration also requires meta-data, such as
+** the origin of a certain configuration entry and how it was derived from other values
+** the sensitivity of some data
+** the provider that have read the data
+** the time, when the data was read
+** the timestamp, when some data may be outdated
+** ...
+
+Basically the same is also the not related to some single configuration key, but also to a whole map.
+The +PropertyProvider+ interface models exact these aspects and looks as illustrated below:
+
+[source,java]
+.Interface PropertyProvider
+--------------------------------------------
+public interface PropertyProvider{
+
+ Optional<String> get(String key);
+ boolean containsKey(String key);
+ Map<String, String> toMap();
+ MetaInfo getMetaInfo();
+
+ default Set<String> keySet();
+ default ConfigChangeSet load();
+ default boolean isMutable();
+ default void apply(ConfigChangeSet change);
+}
+--------------------------------------------
+
+Hereby
+
+* +getMetaInfo()+ return the meta information for the property provider, as well as for individual property key/value pairs.
+* +get, containsKey, keySet+ look similar to the methods on +Map+, though +get+ uses the +Optional+ type introduced
+ with Java 8. This avoids returning +null+ or throwing exceptions in case no such entry is available and also
+ reduced the API's footprint, since default values can be easily implemented by calling +Optional.orElse+.
+* +isMutable()+ allows to easy check, if a property provider is mutable, which is more elegant than catching
+ +NonSupportedOperation+ exception thrown on the according methods of +Map+.
+* +load()+ finally allows to (re)load a property map. It depends on the implementing source, if this operation
+ has any effect. If the map changes an according +ConfigChange+ must be returned, describing the
+ changes applied.
+* +hasSameProperties+ allows to perform a comparison with another provider.
+* +toMap+ allows to extract thing to a +Map+.
+
+This simple model will be used within the spi, where configuration can be injected/provided from external resources.
+But we have seen, that we have to consider additional aspects, such as extendability and type safety. Therefore we
+extend +PropertyMap+ and hereby also apply the 'composite pattern', which results in the following key abstraction.
+
+==== Meta Information
+
+Each instance also provides an instance of +MetaInfo+, which provides meta information for the providers and its properties:
+
+[source,java]
+.Accessing Meta Information
+--------------------------------------------
+PropertyProvider prov = ...;
+MetaInfo metaInfo = prov.getMetaInfo();
+Set<String> keys = metaInfo.keySet(); // returns the attribute keys, for which meta-information is accessible.
+String metaData = metaInfo.get("a.b.c.value"); // access meta information
+String itemName = metaInfo.getName(); // access meta information for the provider
+--------------------------------------------
+
+As we have seen above there is as well a +MetaInfoBuilder+, which must be used to create instances of
++MetaInfo+.
+
+==== Mutability
+
+Property providers optionally may be mutable. This can be checked by calling +boolean isMutable()+. If a provider
+is mutable a +ConfigChangeSet+ can be passed. This change set can then be applied by the provider. On creation
+of the +ConfigChangeSetBuilder+ a provider can pass version information, so _optimistic locking_ can be implemented
+easily:
+
+[source,java]
+.Creating and applying a +ConfigChangeSet+ to a provider
+--------------------------------------------
+PropertyProvider prov = ...;
+ConfigChangeSet changeSet = ConfigChangeSetBuilder.of(provider) // creating a default version
+ .remove("key1ToBeRemoved", +key2ToBeRemoved")
+ .put("key2", "key2Value")
+ .put("key3", 12345)
+ .put("key4", 123.45)
+ .build();
+provider.apply(changeSet);
+--------------------------------------------
+
+[[API CombineProviders]]
+==== Combining Property Providers
+
+Looking at the structures of configuration system used by large companies we typically encounter some kind of configuration
+hierarchies that are combined in arbitrary ways. Users of the systems are typically not aware of the complexities in this
+area, since they simply know the possible locations, formats and the overriding policies. Framework providers on the other
+side must face the complexities and it would be very useful if Tamaya can support here by providing prebuilt functionality
+that helps implementing these aspects. All this leads to the feature set of combining property providers. Hereby the following
+strategies are useful:
+
+* aggregating providers, hereby later providers added
+ ** override any existing entries from earlier providers
+ ** combine conflicting entries from earlier providers, e.g. into a comma-separated structure.
+ ** may throw a ConfigExcepotion ig entries are conflicting
+ ** may only add entries not yet defined by former providers, preventing entries that are already present to be overwritte
+ ** any custom aggregation strategy, which may be a mix of above
+* intersecting providers
+* subtracting providers
+* filtering providers
+
+These common functionality is provided by the +PropertyProviders+ singleton. Additionally to the base strategies above a +MetaInfo+
+instance can be passed optionally as well to define the meta information for the newly created provider instances.
+Let's assume we have two property providers with the following data:
+
+[source,properties]
+.Provider 1
+--------------------------------------------
+a=a
+b=b
+c=c
+g=g
+h=h
+i=i
+--------------------------------------------
+
+[source,properties]
+.Provider 2
+--------------------------------------------
+a=A
+b=B
+c=C
+d=D
+e=E
+f=F
+--------------------------------------------
+
+Looking in detail you see that the entries +a,b,c+ are present in both providers, whereas +d,e,f+ are only present in provider 1,
+and +g,h,i+ only in provider 2.
+
+[source,java]
+.Example Combining PropertyProviders
+--------------------------------------------
+PropertyProvider provider1 = ...
+PropertyProvider provider2 = ...
+
+// aggregate, hereby values from provider 2 override values from provider 1
+PropertyProvider unionOverriding = PropertyProviders.aggregate(AggregationPolicy.OVERRIDE(), provider1, provider2);
+System.out.println("unionOverriding: " + unionOverriding);
+
+// ignore duplicates, values present in provider 1 are not overriden by provider 2
+PropertyProvider unionIgnoringDuplicates = PropertyProviders.aggregate(AggregationPolicy.IGNORE_DUPLICATES(), provider1, provider2);
+System.out.println("unionIgnoringDuplicates: " + unionIgnoringDuplicates);
+
+// this variant combines/maps duplicate values into a new value
+PropertyProvider unionCombined = PropertyProviders.aggregate(AggregationPolicy.COMBINE(), provider1, provider2);
+System.out.println("unionCombined: " + unionCombined);
+
+// This variant throws an exception since there are key/value paris in both providers, but with different values
+try{
+ PropertyProviders.aggregate(AggregationPolicy.EXCEPTION(), provider1, provider2);
+}
+catch(ConfigException e){
+ // expected!
+}
+--------------------------------------------
+
+The example above produces the following outpout:
+
+[source,listing]
+.Example Combining PropertyProviders
+--------------------------------------------
+AggregatedPropertyProvider{
+ (name = dynamicAggregationTests)
+ a = "[a][A]"
+ b = "[b][B]"
+ c = "[c][C]"
+ d = "[D]"
+ e = "[E]"
+ f = "[F]"
+ g = "[g]"
+ h = "[h]"
+ i = "[i]"
+}
+unionOverriding: AggregatedPropertyProvider{
+ (name = <noname>)
+ a = "A"
+ b = "B"
+ c = "C"
+ d = "D"
+ e = "E"
+ f = "F"
+ g = "g"
+ h = "h"
+ i = "i"
+}
+unionIgnoringDuplicates: AggregatedPropertyProvider{
+ (name = <noname>)
+ a = "a"
+ b = "b"
+ c = "c"
+ d = "D"
+ e = "E"
+ f = "F"
+ g = "g"
+ h = "h"
+ i = "i"
+}
+unionCombined: AggregatedPropertyProvider{
+ (name = <noname>)
+ a = "a,A"
+ b = "b,B"
+ c = "c,C"
+ d = "D"
+ e = "E"
+ f = "F"
+ g = "g"
+ h = "h"
+ i = "i"
+}
+--------------------------------------------
+
+No +AggregationPolicy+ is also an interface that can be implemented:
+
+[source,java]
+.AggregationPolicy Interface
+--------------------------------------------
+@FunctionalInterface
+public interface AggregationPolicy {
+ String aggregate(String key, String value1, String value2);
+}
+--------------------------------------------
+
+So we can also define our own aggregation strategy using a Lambda expression:
+
+[source,java]
+.Use a Custom AggregationPolicy
+--------------------------------------------
+PropertyProvider provider1 = ...;
+PropertyProvider provider2 = ...;
+PropertyProvider props = PropertyProviders.aggregate(
+ (k, v1, v2) -> (v1 != null ? v1 : "") + '[' + v2 + "]",
+ MetaInfo.of("dynamicAggregationTests"),
+ props1, props2);
+System.out.println(props);
+--------------------------------------------
+
+Additionally we also pass here an instance of +MetaInfo+. The output of this code snippet is as follows:
+
+[source,listing]
+.Listing of dynamic aggregation policy
+--------------------------------------------
+AggregatedPropertyProvider{
+ (name = dynamicAggregationTests)
+ a = "[a][A]"
+ b = "[b][B]"
+ c = "[c][C]"
+ d = "[D]"
+ e = "[E]"
+ f = "[F]"
+ g = "[g]"
+ h = "[h]"
+ i = "[i]"
+}
+--------------------------------------------
+
+Summarizing the +PropertyProviders+ singleton allows to combine providers in various forms:
+
+[source,listing]
+.Methods provided on PropertyProviders
+--------------------------------------------
+public final class PropertyProviders {
+
+ private PropertyProviders() {}
+
+ public static PropertyProvider fromArgs(String... args) {
+ public static PropertyProvider fromArgs(MetaInfo metaInfo, String... args) {
+ public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, String... paths) {
+ public static PropertyProvider fromPaths(String... paths) {
+ public static PropertyProvider fromPaths(List<String> paths) {
+ public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, List<String> paths) {
+ public static PropertyProvider fromPaths(MetaInfo metaInfo, List<String> paths) {
+ public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<String> paths) {
+ public static PropertyProvider fromUris(URI... uris) {
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, URI... uris) {
+ public static PropertyProvider fromUris(List<URI> uris) {
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, List<URI> uris) {
+ public static PropertyProvider fromUris(MetaInfo metaInfo, URI... uris) {
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, URI... uris) {
+ public static PropertyProvider fromUris(MetaInfo metaInfo, List<URI> uris) {
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<URI> uris) {
+ public static PropertyProvider fromMap(Map<String, String> map) {
+ public static PropertyProvider fromMap(MetaInfo metaInfo, Map<String, String> map) {
+ public static PropertyProvider empty() {
+ public static PropertyProvider emptyMutable() {
+ public static PropertyProvider empty(MetaInfo metaInfo) {
+ public static PropertyProvider emptyMutable(MetaInfo metaInfo) {
+ public static PropertyProvider fromEnvironmentProperties() {
+ public static PropertyProvider fromSystemProperties() {
+ public static PropertyProvider freezed(PropertyProvider provider) {
+ public static PropertyProvider aggregate(AggregationPolicy mapping, MetaInfo metaInfo, PropertyProvider... providers){
+ public static PropertyProvider aggregate(PropertyProvider... providers) {
+ public static PropertyProvider aggregate(List<PropertyProvider> providers) {
+ public static PropertyProvider aggregate(AggregationPolicy mapping, PropertyProvider... propertyMaps) {
+ public static PropertyProvider aggregate(AggregationPolicy mapping, List<PropertyProvider> providers) {
+ public static PropertyProvider mutable(PropertyProvider provider) {
+ public static PropertyProvider intersected(AggregationPolicy aggregationPolicy, PropertyProvider... providers) {
+ public static PropertyProvider intersected(PropertyProvider... providers) {
+ public static PropertyProvider subtracted(PropertyProvider target, PropertyProvider... providers) {
+ public static PropertyProvider filtered(Predicate<String> filter, PropertyProvider provider) {
+ public static PropertyProvider contextual(Supplier<PropertyProvider> mapSupplier,
+ Supplier<String> isolationKeySupplier) {
+ public static PropertyProvider delegating(PropertyProvider mainMap, Map<String, String> parentMap) {
+ public static PropertyProvider replacing(PropertyProvider mainMap, Map<String, String> replacementMap) {
+}
+--------------------------------------------
+
+
+[[API Configuration]]
+=== Configuration
+==== Basic Model
+
+Configuration inherits all basic features from +PropertyProvider+, but additionally adds functionality for
+type safety and extension mechanisms:
+
+[source,java]
+.Interface Configuration
+--------------------------------------------
+public interface Configuration extends PropertyProvider{
+
+ default OptionalBoolean getBoolean(String key);
+ default OptionalInt getInteger(String key);
+ default OptionalLong getLong(String key);
+ default OptionalDouble getDouble(String key);
+ default <T> Optional<T> getAdapted(String key, PropertyAdapter<T> adapter);
+ <T> Optional<T> get(String key, Class<T> type);
+
+ // accessing areas
+ default Set<String> getAreas();
+ default Set<String> getTransitiveAreas();
+ default Set<String> getAreas(final Predicate<String> predicate);
+ default Set<String> getTransitiveAreas(Predicate<String> predicate);
+ default boolean containsArea(String key);
+
+ // extension points
+ default Configuration with(ConfigOperator operator);
+ default <T> T query(ConfigQuery<T> query);
+
+ // versioning
+ default String getVersion(){return "N/A";}
+ void addPropertyChangeListener(PropertyChangeListener l);
+ void removePropertyChangeListener(PropertyChangeListener l);
+
+ // singleton accessors
+ public static boolean isDefined(String name);
+ public static <T> T current(String name, Class<T> template);
+ public static Configuration current(String name);
+ public static Configuration current();
+ public static <T> T current(Class<T> type){
+ public static void configure(Object instance);
+ public static String evaluateValue(String expression);
+ public static String evaluateValue(Configuration config, String expression);
+ public static void addGlobalPropertyChangeListener(PropertyChangeListener listener);
+ public static void removeGlobalPropertyChangeListener(PropertyChangeListener listener);
+}
+--------------------------------------------
+
+Hereby
+
+* +XXX getXXX(String)+ provide type safe accessors for all basic wrapper types of the JDK.
+* +getAdapted+ allow accessing any type, hereby also passing a +PropertyAdapter+ that converts
+ the configured literal value to the type required.
+* +getAreas()+, +getTransitiveAreas()+ allow to examine the hierarchical tree modeled by the configuration tree.
+ Optionally also predicates can be passed to select only part of the tree to be returned.
+* +containsArea+ allows to check, if an area is defined.
+* +with, query+ provide the extension points for adding additional functionality.
+
+* the static accessor methods define:
+ ** +current(), current(Class), current(String), current(String, Class)+ return the configuration valid for the current runtime environment.
+ ** +addPropertyChangeListener, removePropertyChangeListener+ allow to register or unregister
+ global config change listener instances.
+ ** evaluateValue allows to evaluate a configuration expression based on a given configuration.
+ ** +configure+ performs injection of configured values.
+
+[[TypeConversion]]
+==== Type Conversion
+
+Configuration extend +PropertyProvider+ and add additional support for non String types. This is achieved
+with the help of +PropertyAdapter+ instances:
+
+[source,java]
+.PropertyAdapter
+--------------------------------------------
+@FunctionalInterface
+public interface PropertyAdapter<T>{
+ T adapt(String value);
+}
+--------------------------------------------
+
+PropertyAdapter instances can be implemented manually or registered and accessed from the
++PropertyAdapers+ singleton. Hereby the exact mechanism is determined by the API backing up the singleton.
+By default corresponding +PropertyAdapter+ instances can be registered using the Java +ServiceLoader+
+mechanism, or programmatically ba calling the +register(Class, PropertyAdapter)+ method.
+
+[source,java]
+--------------------------------------------
+public final class PropertyAdapters{
+ public static <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> adapter);
+ public static boolean isTargetTypeSupported(Class<?> targetType);
+ public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType);
+ public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType, WithPropertyAdapter annotation);
+}
+--------------------------------------------
+
+Whereas this mechanism per se looks not very useful it's power shows up when combining it with the annotations
+API provided, e.g. look at the following annotated class:
+
+[source,java]
+.Annotated Example Class
+--------------------------------------------
+public class ConfiguredClass{
+
+ @ConfiguredProperty
+ private String testProperty;
+
+ @ConfiguredProperty("a.b.c.key1")
+ @DefaultValue("The current \\${JAVA_HOME} env property is ${env:JAVA_HOME}.")
+ String value1;
+
+ @ConfiguredProperty("a.b.c.key2")
+ private int value2;
+
+ @ConfiguredProperty
+ @DefaultValue("http://127.0.0.1:8080/res/api/v1/info.json")
+ private URL accessUrl;
+
+ @ConfiguredProperty
+ @DefaultValue("5")
+ private Integer int1;
+
+ @ConfiguredProperty("a.b.customType")
+ private MyCustomType myCustomType;
+
+ @ConfiguredProperty("BD")
+ private BigDecimal bigNumber;
+
+ ...
+}
+--------------------------------------------
+
+The class does not show all the possibilities that are provided, but it shows that arbitrary types can be supported easily.
+This applied similarly to collection types, whereas collections are more advanced and therefore described in a separate section
+later.
+
+Given the class above and the current configuration can provide the values required, configuring an instance of the
+class is simple:
+
+[source,java]
+.Configuring the Example Class
+--------------------------------------------
+ConfiguredClass classInstance = new ConfiguredClass();
+Configuration.configure(configuredClass);
+--------------------------------------------
+
+Additional types can transparently be supported by implementing and registering corresponding SPI instances. This is explained
+in the SPI documentation of {name}.
+
+==== Extension Points
+
+We are well aware of the fact that this library will not be able to cover all kinds of use cases. Therefore
+we have added similar functional extension mechanisms that were used in other areas of the Java eco-system as well:
+
+* +ConfigOperator+ define unary operations on +Configuration+. They can be used for filtering, implementing
+ configuration views, security interception etc.
+* +ConfigQuery+ defines a function returning any kind of result based on a configuration instance. Typical
+ use cases of queries could be the implementation of configuration SPI instances that are required
+ by other libraries or frameworks.
+
+Both interfaces hereby are defined as functional interfaces:
+
+[source,java]
+.ConfigOperator and ConfigQuery
+--------------------------------------------
+@FunctionalInterface
+public interface ConfigOperator{
+ Configuration operate(Configuration config);
+}
+
+@FunctionalInterface
+public interface ConfigQuery<T>{
+ T query(Configuration config);
+}
+--------------------------------------------
+
+Both interfaces can be applied on a +Configuration+ instance:
+
+[source,java]
+.Applying Config operators and queries
+--------------------------------------------
+Configuration secured = Configuration.of().apply(ConfigSecurity::secure);
+ConfigSecurity securityContext = Configuration.of().query(ConfigSecurity::targetSecurityContext);
+--------------------------------------------
+
+NOTE: +ConfigSecurity+ is an arbitrary class.
+
+=== Configuration Injection
+
+The +Configuration+ interface provides static methods that allow to anykind of instances be configured
+ny just passing the instances calling +Configuration.configure(instance);+. The classes passed hereby must
+be annotated with +@ConfiguredProperty+ to define the configured properties. Hereby this annotation can be
+used in multiple ways and combined with other annotations such as +@DefaultValue+,
++@WithLoadPolicy+, +@WithConfig+, +@WithConfigOperator+, +@WithPropertyAdapter+.
+
+To illustrate the mechanism below the most simple variant of a configured class is given:
+
+[source,java]
+.Most simple configured class
+--------------------------------------------
+pubic class ConfiguredItem{
+ @ConfiguredProperty
+ private String aValue;
+}
+--------------------------------------------
+
+When this class is configured, e.g. by passing it to +Configuration.configure(Object)+,
+the following is happening:
+
+* The current valid +Configuration+ is evaluated by calling +Configuration cfg = Configuration.of();+
+* The current property value (String) is evaluated by calling +cfg.get("aValue");+
+* if not successful, an error is thrown (+ConfigException+)
+* On success, since no type conversion is involved, the value is injected.
+* The configured bean is registered as a weak change listener in the config system's underlying
+ configuration, so future config changes can be propagated (controllable by applying the
+ +@WithLoadPolicy+ annotation).
+
+In the next example we explicitly define the property value:
+[source,java]
+--------------------------------------------
+pubic class ConfiguredItem{
+
+ @ConfiguredProperty
+ @ConfiguredProperty("a.b.value")
+ @configuredProperty("a.b.deprecated.value")
+ @DefaultValue("${env:java.version}")
+ private String aValue;
+}
+--------------------------------------------
+
+Within this example we evaluate multiple possible keys. Evaluation is aborted if a key could be successfully
+resolved. Hereby the ordering of the annotations define the ordering of resolution, so in the example above
+resolution equals to +"aValue", "a.b.value", "a.b.deprecated.value"+. If no value could be read
+from the configuration, it uses the value from the +@DefaultValue+ annotation. Interesting here
+is that this value is not static, it is evaluated by calling +Configuration.evaluateValue(Configuration, String)+.
+
+=== Environment
+
+The environment basically is also a kind of property/value provider similar to +System.getProperties()+ and +System
+.getenv()+ in the JDK. Nevertheless it provides additional functionality:
+
+[source,java]
+.Interface Environment
+--------------------------------------------
+public interface Environments {
+
+ String getEnvironmentType();
+ String getEnvironmentId();
+ Environment getParentEnvironment();
+
+ Optional<String> get(String key);
+ boolean containsKey(String key);
+ Set<String> keySet();
+ Map<String,String> toMap();
+
+ public static Environment current(){
+ public static Environment getRootEnvironment(){
+ public static List<String> getEnvironmentTypeOrder(){
+ public static List<String> getEnvironmentHierarchy(){
+ public static Optional<Environment> getInstance(String environmentType, String contextId){
+ public static Set<String> getEnvironmentContexts(String environmentType){
+ public static boolean isEnvironmentActive(String environmentType){
+--------------------------------------------
+
+* environments are hierarchical. Hereby all environments inherit from the root environment. The root environment
+ hereby must contain
+ ** all JDK's system properties, with same keys, values
+ ** all JDK's environment properties, prefixed with +env:+.
+ ** additional root properties are allowed as well.
+* the root environment is always directly accessible by calling +Environment.getRootEnvironment()+
+* the current environment can be accessed by calling +Environment.of()+.
+* each environment also defines a +Stage+ (implementing +StageSupplier+). Hereby, if not set explicitly the +Stage+ is inherited from the root
+ environment. Consequently the root environment must provide a +Stage+, which by default is +Stage.development()+.
+
+Additionally each environment instance is uniquely identified by the environment type (accessible from
++getEnvironmentType()+ and the environment id (accessible from +getEnvironmentId()+). So it is possible to access
+an +Environment+ by calling +of(String environmentType, String environmentId)+. Implementations may restrict access
+to environments depending on the current runtime environment (runtime context) active. The API does
+not require further aspects.
+
+The call to +getEnvironmentIds(String)+ returns all context ids of the known +Environment+ instances
+of a given type. E.g. assuming there is an environment type +war+ calling +Environment.getEnvironmentIds("war")+
+may return +"/web/app1", "/web/app2"+ (assuming the war context ids equal the web applications root contexts).
+
+All environments are basically ordered. The ordering can be accessed by calling +getEnvironmentTypeOrder()+. Hereby
+not every environment type in a hierarchy must necessarily present. This is reflected by +getEnvironmentHierarchy()+
+which returns the environment type ids in order, but only containing the types of the environments
+currently present and accessible in the hierarchy. As an example an environment type order in an advanced
+use case could be something like +"root","ear","war","saas","user"+, whereas the concrete environment type hierarchy
+may be +"root","war","saas"+, because the application was not included
+in an additional ear archive and no user is currently active (anonymous). The call to +isEnvironmentActive(String)+
+allows to determine if an environment of the given type is currently active.
+Finally the environment hierarchy is of course similarly reflected by the relationship (+getParentEnvironment()+).
+The following code should illustrate some of these concepts:
+
+[source,java]
+.Interface Environment
+--------------------------------------------
+List<String> envHierarchy = Environment.getEnvironmentHierarchy();
+ // -> "root","war","saas"
+Environment env = Environment.of();
+System.out.println(env.getEnvironmentContext()); // saas
+System.out.println(env.getEnvironmentId()); // mysolution_pro
+env = env.getParentEnvironment();
+System.out.println(env.getEnvironmentContext()); // war
+System.out.println(env.getEnvironmentId()); // pro
+env = env.getParentEnvironment();
+System.out.println(env.getEnvironmentContext()); // root
+System.out.println(env.getEnvironmentId()); // system
+env = env.getParentEnvironment();
+// env is null now!
+--------------------------------------------
+
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/combine-configs.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/combine-configs.adoc b/docs/src/main/asciidoc/usecases/se/combine-configs.adoc
new file mode 100644
index 0000000..2d83ab7
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/combine-configs.adoc
@@ -0,0 +1,14 @@
+=== Combine Configurations
+
+Users want to be able to combine different configurations to a new configuration instance.
+Hereby the resulting configuration can be
+
+* a union of both, ignoring duplicates (and optionally log them)
+* a union of both, duplicates are ignored
+* a union of both, conflicts are thrown as ConfigException
+* an intersection of both, containing only keys present and equal in both configurations
+* an arbitrary mapping or filter, modelled by an +CombinationPolicy+, which basically can be modelled
+ as +BiFunction<String, String, String>+, hereby
+ ** a result of +null+ will remove the key
+ ** any other result will use the value returned as final value of the combination.
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/context-dependent-configuration.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/context-dependent-configuration.adoc b/docs/src/main/asciidoc/usecases/se/context-dependent-configuration.adoc
new file mode 100644
index 0000000..737232f
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/context-dependent-configuration.adoc
@@ -0,0 +1,7 @@
+=== Context Dependent Configuration
+
+In multi tenancy setups or complex systems a hierarchical/graph model of contexts for configurations is required, or different runtime contexts are executed in parallel
+within the same VN. What sounds normal for EE also may be the case for pure SE environments:
+
+* Users want to be able to model different layers of runtime context
+* Users want to identiofy the current layer, so configuration used may be adapted.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/dynamic-provisioning.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/dynamic-provisioning.adoc b/docs/src/main/asciidoc/usecases/se/dynamic-provisioning.adoc
new file mode 100644
index 0000000..2facc67
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/dynamic-provisioning.adoc
@@ -0,0 +1,17 @@
+=== Dynamic Provisioning (UC8)
+
+In Cloud Computing, especially the PaaS and SaaS areas a typical use case would be that an application (or server)
+is deployed, configured and started dynamically. Typically things are controlled by some "active controller components",
+which are capable of
+* creating new nodes (using IaaS services)
+* deploying and starting the required runtime platform , e.g. as part of a PaaS solution.
+* deploying and starting the application modules.
+
+All these steps require some kind of configuration. As of today required files are often created on the target node
+before the systems are started, using proprietary formats and mechanism. Similarly accessing the configuration in place
+may require examining the file system or using again proprietary management functions. Of course, a configuration
+solution should not try to solve that, but it can provide a significant bunch of functionality useful in such scenarios:
+
+* provide remote capabilities for configuration
+* allow configuration to be updated remotely.
+* allow client code to listen for configuration changes and react as needed.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/external-configuration.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/external-configuration.adoc b/docs/src/main/asciidoc/usecases/se/external-configuration.adoc
new file mode 100644
index 0000000..ea2e687
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/external-configuration.adoc
@@ -0,0 +1,6 @@
+=== External Configuration
+
+Users want to be able to replace, override, extend or adapt any parts or all of an existing configuration from
+external sources.
+This also must be the case for multi-context environments, where the context identifiers are
+the only way to link to the correct remote configuration.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/formats.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/formats.adoc b/docs/src/main/asciidoc/usecases/se/formats.adoc
new file mode 100644
index 0000000..7383b0d
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/formats.adoc
@@ -0,0 +1,7 @@
+=== Configuration Formats
+
+Users want to be able to use the format they prefer.
+* properties, xml-properties and ini-format should be supported by default
+* Other/custom formats should be easily addable by registering or providing the format on configuration evaluation (read).
+* When possible Tamaya should figure out which format to be used and how the InputStream should be correctly
+ interpreted.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/injection.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/injection.adoc b/docs/src/main/asciidoc/usecases/se/injection.adoc
new file mode 100644
index 0000000..4468d6c
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/injection.adoc
@@ -0,0 +1,31 @@
+=== Configuration Injection
+
+Users want to be able to polulate configured items by injecting configured values. Hereby
+
+* the lifecycle of the instances is not managed by Tamaya
+* all references to items configured are managed as weak references, to prevent memoryleaks.
+* Injection should if possible evaluate the properties by defaults. Even properties without
+ any annotations are possible.
+* Users expect to exclude certain properties from calculation
+* Beside injection of properties it is also possible to call setter methods with one parameter similarly.
+* Basically injection is performed, when the instance is passed to the Tamaya configuration system. It should also
+ be possible to inject/provide final values, especially Strings. Changes on configured values should be
+ reflected in the current value. Setters methods similarly can be called again, with the new values, on changes.
+* Users expect to control dynamic values and recall of setter methods, basically the following strategies should be
+ supported:
+ ** inject only once and ignore further changes.
+ ** reinject/reinitialize on each change
+
+* Dynamic Values can easily be modeled as +ConfiguredValue+ instances, which should have the following functionality:
+ ** access the current value
+ ** access the new value
+ ** access the latest value access time in ms
+ ** access the latest value update time in ms
+ ** evaluate easily if the value has changed since the last access
+ ** accept the change
+ *** as a shortcut it should be possible to accept the change on access of the value implicitly, hereby always accessing
+ the latest valid value.
+ ** ignore the change
+ ** register +Consumer<DynamicValue>+ liasteners to listen on the changes (ans also removing them later again).
+
+All observing functionality can be done completely asynchronously and in parallel.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/java8.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/java8.adoc b/docs/src/main/asciidoc/usecases/se/java8.adoc
new file mode 100644
index 0000000..fe892cb
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/java8.adoc
@@ -0,0 +1,13 @@
+=== Java 8 Functional Support
+
+Users want to be able to benefit from the new programming styles introduced with Java 8. Configuration
+should provide extension points for different aspects, where additional code can be hooked in easily.
+In short: were possible functional interfaces should be modelled.
+
+Examples:
+* code that converts a configuration to another kind of configuration: +UnaryOperator<Configuration>+
+* code that creates any kind of result based on a configuration: +Function<Configuration,T>+
+* Adapters for type conversion are defined as +Function<String,T>+
+* Key, value filters ccan be modelled as +BiFunction<String,String,String>+
+* etc.
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/locations.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/locations.adoc b/docs/src/main/asciidoc/usecases/se/locations.adoc
new file mode 100644
index 0000000..f18d7f6
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/locations.adoc
@@ -0,0 +1,9 @@
+=== Configuration Locations
+
+Users want to be able to
+* read configuration from different locations.
+* By default classpath and file resources are
+ supported. But similarly remote access using a JSON ReST call should be possible.
+* Tamaya should define a JSON and XML format for configuration.
+* Configuration locations should be scannable using ant-styled resource patterns, if possible.
+* Scanning and reading logic can be modularized by using a +ConfigReader+ interface.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/management.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/management.adoc b/docs/src/main/asciidoc/usecases/se/management.adoc
new file mode 100644
index 0000000..ff997a5
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/management.adoc
@@ -0,0 +1,7 @@
+=== MX/ReST Management
+
+Users want to be have comprehensive management support, which should allow
+
+* to change configuration
+* to remove configuration
+* to view configuration and its provider details
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/minimal-propertysource.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/minimal-propertysource.adoc b/docs/src/main/asciidoc/usecases/se/minimal-propertysource.adoc
new file mode 100644
index 0000000..ee185a0
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/minimal-propertysource.adoc
@@ -0,0 +1,6 @@
+=== Minimal Property Source SPI
+
+Users expect that implementing an additional configuration property source is as easy as possible.
+So there should be an SPI defined that allows any kind of data source to be used for providing configuration data.
+The interface to be implemented is expected to be minimal to reduce the implementation burden. Default
+methods should be used where possible, so only a few methods are expected to be required to implement.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/multiple-configurations.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/multiple-configurations.adoc b/docs/src/main/asciidoc/usecases/se/multiple-configurations.adoc
new file mode 100644
index 0000000..2ee133f
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/multiple-configurations.adoc
@@ -0,0 +1,14 @@
+=== Multiple Configurations
+
+When systems grow they must be modularized to keep control. Whereas that sounds not really fancy, it leads to additional
+aspects to be considered by a configuration system.
+
+* Different code modules, libraries, plugins or products want to have their "own" separated configuration.
+* Similar it should be possible to add fully specific additional configurations.
+
+The default configuration hereby should always be present, whereas additional configurations are optional.
+Users want to be able to check the availability of such an additional configuration.
+
+Of course, additional configuration must be identifiable. The best way to do is to be discussed, nevertheless the
+mechanism must not depend on Java EE and the identifying keys must be serializable easily.
+Basically simple names are sufficient and woukld provide exact this required functionality.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/scannable-properties.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/scannable-properties.adoc b/docs/src/main/asciidoc/usecases/se/scannable-properties.adoc
new file mode 100644
index 0000000..e2a6b64
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/scannable-properties.adoc
@@ -0,0 +1,4 @@
+=== Scannable Properties
+
+If possible configuration should be scannable, meaning it should be possible to evaluate the keys available.
+The corresponding capabilities should be accessible by a +isScannable()+ method.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/service-context.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/service-context.adoc b/docs/src/main/asciidoc/usecases/se/service-context.adoc
new file mode 100644
index 0000000..31ffaaa
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/service-context.adoc
@@ -0,0 +1,14 @@
+=== Adaptable Service Context
+
+Tamaya should support an adaptable +ServiceContext+ to resolve any kind of implememntation services, both API services as core
+framework services. The +ServiceContext+ should be dynamically replecable by configuring an alternate instance of
+using the Java *ServiceContet+.
+This decouples component usage from its load and management part and als greatly simplifies integration with
+new/alternate runtime environments.
+The service context is exptected to provide
+
+* single singleton instances: these service can be cached.
+* access to multiple instances which implement some commons SPI interface.
+* as useful priorization of components is done by the model itself.
+
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/simple-access.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/simple-access.adoc b/docs/src/main/asciidoc/usecases/se/simple-access.adoc
new file mode 100644
index 0000000..bc1bf59
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/simple-access.adoc
@@ -0,0 +1,18 @@
+=== Simple Access
+
+Users want to be able to access configuration in a unified way both in SE and EE. EE may provide additional
+mechanism, such as injection, but the SE mechanisms should work as well, so any code written in SE is fully
+portable to EE as well.
+This can only be achieved by providing a static accessor, e.g.
+
+Configuration config = Configuration.current();
+
+The call above should work exactly the same in EE. To enable this the static call must be delegated in the
+internals of the singleton, depending on the runtime. In EE the executing component can behave contextually,
+or even be loaded within the CDI environment (at least for post loading, application runtime aspects, but not earlier).
+
+Additionally in EE it should also be possible to inject Configuration, which gives you the same results as the call
+above:
+
+@Inject
+private Configuration cfg;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/simple-property-access.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/simple-property-access.adoc b/docs/src/main/asciidoc/usecases/se/simple-property-access.adoc
index 32c2389..81fb879 100644
--- a/docs/src/main/asciidoc/usecases/se/simple-property-access.adoc
+++ b/docs/src/main/asciidoc/usecases/se/simple-property-access.adoc
@@ -1,2 +1,9 @@
=== Simple Lookup of Properties
+Users just want to create a configuration ad hoc, from given property files. The
+files could be locally in the file system, on the classpath.
+
+Tamaya should provide a simple Java API for accessing key/value based configuration. Hereby users want to access
+properties as simple String values.
+
+Hereby returning Java 8 Optional values must be considered as well, instead of returning +null+.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/templates.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/templates.adoc b/docs/src/main/asciidoc/usecases/se/templates.adoc
new file mode 100644
index 0000000..0aff6c3
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/templates.adoc
@@ -0,0 +1,11 @@
+=== Configuration Templates
+
+Users want to be able to let Tamaya implement an interface template based on configuration.
+This use case is pretty similar to the injection use case. Basically the values are not injected,
+but cached within the template proxy returned, e.g. as +DynamicValue+.
+Similarly it could even be possible to define callback methods (default methods)
+or register listeners to DynamicValue instances returned.
+
+Templates hereby can easily be managed, but provide a excellent mechanism to provide type-safe configuration
+to clients in a very transparent way. Details can be controlled by using the same annotations as for
+normal configuration injection.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/type-safe-properties.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/type-safe-properties.adoc b/docs/src/main/asciidoc/usecases/se/type-safe-properties.adoc
new file mode 100644
index 0000000..e71d31c
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/type-safe-properties.adoc
@@ -0,0 +1,10 @@
+=== Type Safe Properties
+
+Users just want to access properties not only as Strings, but let Tamaya do the conversion to the required
+or the configred target type. By defauklt all java.lang wrapper and primitive types should be supported, but also
+other common types like date/time types, math numeric types and more.
+
+It must be possible that users can register their own custom types.
+
+Finally users also want to be able to dynamically provide or override type adaption (conversion), when reading a value,
+for a certain key/value pair.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/se/value-placeholders.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/se/value-placeholders.adoc b/docs/src/main/asciidoc/usecases/se/value-placeholders.adoc
new file mode 100644
index 0000000..57857a8
--- /dev/null
+++ b/docs/src/main/asciidoc/usecases/se/value-placeholders.adoc
@@ -0,0 +1,8 @@
+=== Value Placeholders
+
+Users just want to to be able to add placeholders to the values of configuration (not the keys). The mechanisms for
+resolving the placeholders hereby should be not constraint to one single lanmguage like EL. Instead of different
+replacement strategies should be selectable, e.g. by prefixing an expression with the name of the resolver that
+should do the work (eg +"blabla ${env:HOME} using Java version ${sys:java.version}."+.
+This allows resolution mechanism to be isolated easily and also allows to use simpler mechanisms, if no more complex
+ones like EL are required. This is especially useful to deal with low resource environment like ME.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/usecases/usecases.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/usecases/usecases.adoc b/docs/src/main/asciidoc/usecases/usecases.adoc
index b3bad6b..3fd8ef2 100644
--- a/docs/src/main/asciidoc/usecases/usecases.adoc
+++ b/docs/src/main/asciidoc/usecases/usecases.adoc
@@ -24,4 +24,23 @@ toc::[]
== Use Cases for Java SE Environments
+include::se/simple-access.adoc[]
include::se/simple-property-access.adoc[]
+include::se/value-placeholders.adoc[]
+include::se/type-safe-properties.adoc[]
+include::se/templates.adoc[]
+include::se/java8.adoc[]
+include::se/locations.adoc[]
+include::se/formats.adoc[]
+include::se/multiple-configurations.adoc[]
+include::se/external-configuration.adoc[]
+include::se/context-dependent-configuration.adoc[]
+include::se/dynamic-provisioning.adoc[]
+include::se/minimal-propertysource.adoc[]
+include::se/scannable-properties.adoc[]
+include::se/combine-configs.adoc[]
+include::se/management.adoc[]
+include::se/service-context.adoc[]
+include::se/injection.adoc[]
+
+
[2/4] incubator-tamaya git commit: TAMAYA-19: Streamlined API and
impl.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/design/3_Core.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/design/3_Core.adoc b/docs/src/main/asciidoc/design/3_Core.adoc
new file mode 100644
index 0000000..90794be
--- /dev/null
+++ b/docs/src/main/asciidoc/design/3_Core.adoc
@@ -0,0 +1,356 @@
+// 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.
+<<<
+[[Core]]
+== Tamaya Core
+=== Overview
+
+Tamaya Core provides an implementation of the Tamaya Configuration API and adds additional functionality and
+building blocks for supporting SPI implementations.
+
+Tamaya Core contains the following artifacts:
+
+* +DefaultConfigurationSpi, DefaultPropertyAdapterSpi+ implement the main API interfaces, backing up +Configuration+ and
+ +PropertyAdapter+
+* tbd
+
+The SPI contains the following core concepts/artifacts:
+
+* tbd
+
+* +org.apache.tamaya.core+ contains the main abstractions +Configuration, ConfigQuery, PropertyAdapter,
+ PropertySource+
+* +org.apache.tamaya.core.spi+ contains the SPI interfaces to be implemented by implementations and the +ServiceContext+ mechanism.
+
+
+[[CorePropertyAdapterSpi]]
+=== PropertyAdapter (PropertyAdapterSpi) Implementation
+
+tbd
+
+[[CoreConfigurationSpi]]
+=== Configuration (ConfigurationSpi) Implementation
+
+tbd
+
+
+[[Core ConfigurationBuilder]]
+=== Building Simple Configuration
+
+Looking at the structures of configuration system used by large companies we typically encounter some kind of configuration
+hierarchies that are combined in arbitrary ways. Users of the systems are typically not aware of the complexities in this
+area, since they simply know the possible locations, formats and the overriding policies. Framework providers on the other
+side must face the complexities and it would be very useful if Tamaya can support here by providing prebuilt functionality
+that helps implementing these aspects. All this leads to the feature set of combining property sources. Hereby the following
+strategies are useful:
+
+* aggregating configurations, hereby later configurations added
+ ** override any existing entries from earlier configurations
+ ** combine conflicting entries from earlier configurations, e.g. into a comma-separated structure.
+ ** may throw a ConfigException ig entries are conflicting
+ ** may only add entries not yet defined by former providers, preventing entries that are already present to be overwrite
+ ** any custom aggregation strategy, which may be a mix of above
+* intersecting configurations
+* subtracting configurations
+* filtering configurations
+
+These common functionality is provided by +ConfigurationBuilder+ instances. Additionally to the base strategies above a
++MetaInfo+ instance can be passed optionally as well to define the meta information for the newly created configuration.
+Let's assume we have two configurations with the following data:
+
+[source,properties]
+.Configuration 1
+--------------------------------------------
+a=a
+b=b
+c=c
+g=g
+h=h
+i=i
+--------------------------------------------
+
+[source,properties]
+.Configuration 2
+--------------------------------------------
+a=A
+b=B
+c=C
+d=D
+e=E
+f=F
+--------------------------------------------
+
+Looking in detail you see that the entries +a,b,c+ are present in both configurations, whereas +d,e,f+ are only present in Configuration 1,
+and +g,h,i+ only in Configuration 2.
+
+[source,java]
+.Example Combining Configurations
+--------------------------------------------
+Configuration cfg1 = ...
+Configuration cfg2 = ...
+
+// aggregate, hereby values from Configuration 2 override values from Configuration 1
+Configuration unionOverriding = ConfigurationBuilder.of().aggregate(cfg1, cfg2).build();
+System.out.println("unionOverriding: " + unionOverriding);
+
+// ignore duplicates, values present in Configuration 1 are not overriden by Configuration 2
+Configuration unionIgnoringDuplicates = ConfigurationBuilder.of()
+ .withAggregationPolicy(AggregationPolicy.IGNORE_DUPLICATES).aggregate(cfg1, cfg2).build();
+System.out.println("unionIgnoringDuplicates: " + unionIgnoringDuplicates);
+
+// this variant combines/maps duplicate values into a new value
+Configuration unionCombined = ConfigurationBuilder.of().withAggregationPolicy(AggregationPolicy.COMBINE)
+ .aggregate(cfg1, cfg2);
+System.out.println("unionCombined: " + unionCombined);
+
+// This variant throws an exception since there are key/value paris in both providers, but with different values
+try{
+ ConfigurationBuilder.of().withAggregationPolicy(AggregationPolicy.EXCEPTION).aggregate(provider1, provider2)
+ .build();
+}
+catch(ConfigException e){
+ // expected!
+}
+--------------------------------------------
+
+The example above produces the following outpout:
+
+[source,listing]
+.Example Combining Configurations
+--------------------------------------------
+AggregatedConfiguration{
+ (name = dynamicAggregationTests)
+ a = "[a][A]"
+ b = "[b][B]"
+ c = "[c][C]"
+ d = "[D]"
+ e = "[E]"
+ f = "[F]"
+ g = "[g]"
+ h = "[h]"
+ i = "[i]"
+}
+unionOverriding: AggregatedConfigurations{
+ (name = <noname>)
+ a = "A"
+ b = "B"
+ c = "C"
+ d = "D"
+ e = "E"
+ f = "F"
+ g = "g"
+ h = "h"
+ i = "i"
+}
+unionIgnoringDuplicates: AggregatedConfigurations{
+ (name = <noname>)
+ a = "a"
+ b = "b"
+ c = "c"
+ d = "D"
+ e = "E"
+ f = "F"
+ g = "g"
+ h = "h"
+ i = "i"
+}
+unionCombined: AggregatedConfigurations{
+ (name = <noname>)
+ a = "a,A"
+ b = "b,B"
+ c = "c,C"
+ d = "D"
+ e = "E"
+ f = "F"
+ g = "g"
+ h = "h"
+ i = "i"
+}
+--------------------------------------------
+
+No +AggregationPolicy+ is also a functional interface that can be implemented:
+
+[source,java]
+.AggregationPolicy Interface
+--------------------------------------------
+@FunctionalInterface
+public interface AggregationPolicy {
+ String aggregate(String key, String value1, String value2);
+}
+--------------------------------------------
+
+So we can also define our own aggregation strategy using a Lambda expression:
+
+[source,java]
+.Use a Custom AggregationPolicy
+--------------------------------------------
+Configuration cfg1 = ...;
+Configuration cfg2 = ...;
+Configuration config = ConfigurationBuilder.of("dynamicAggregationTests")
+ .withAggregationPolicy((k, v1, v2) -> (v1 != null ? v1 : "") + '[' + v2 + "]")
+ .aggregate(cfg1, cfg2).build();
+System.out.println(config);
+--------------------------------------------
+
+The output of this code snippet is as follows:
+
+[source,listing]
+.Listing of dynamic aggregation policy
+--------------------------------------------
+AggregatedConfiguration{
+ (name = dynamicAggregationTests)
+ a = "[a][A]"
+ b = "[b][B]"
+ c = "[c][C]"
+ d = "[D]"
+ e = "[E]"
+ f = "[F]"
+ g = "[g]"
+ h = "[h]"
+ i = "[i]"
+}
+--------------------------------------------
+
+Summarizing the +ConfigurationBuilder+ allows to combine providers in various forms:
+
+[source,listing]
+.Methods provided on PropertySources
+--------------------------------------------
+public final class ConfigurationBuilder {
+
+ private ConfigurationBuilder() {}
+
+ public static ConfigurationBuilder of();
+ public static ConfigurationBuilder of(PropertySource config);
+ public static ConfigurationBuilder of(String name);
+
+ public ConfigurationBuilder withAggregationPolicy(AggregationPolicy aggregationPolicy);
+ public ConfigurationBuilder withName(String name);
+
+ public ConfigurationBuilder addArgs(String... args);
+ public ConfigurationBuilder addPaths(List<String> paths);
+ public ConfigurationBuilder addUrls(URL... urls);
+ public ConfigurationBuilder addUrls(List<URL> urls);
+ public ConfigurationBuilder addMap(Map<String, String> map);
+
+ public Configuration empty();
+ public Configuration empty(String name);
+ public ConfigurationBuilder addEnvironmentProperties();
+ public ConfigurationBuilder addSystemProperties();
+ public ConfigurationBuilder aggregate(AggregationPolicy policy, Configuration... configs){
+ public ConfigurationBuilder aggregate(AggregationPolicy policy, List<Configuration> configs) {
+ public ConfigurationBuilder intersected(Configuration... providers) {
+ public ConfigurationBuilder subtracted(Configuration target, Configuration... providers) {
+ public ConfigurationBuilder filtered(Predicate<String> filter, Configuration config) {
+ public ConfigurationBuilder contextual(Supplier<Configuration> mapSupplier,
+ Supplier<String> isolationKeySupplier) {
+ public ConfigurationBuilder delegating(Configuration mainMap, Map<String, String> parentMap) {
+ public ConfigurationBuilder replacing(Configuration mainMap, Map<String, String> replacementMap);
+
+ public Configuration build();
+ public Configuration buildFrozen();
+}
+--------------------------------------------
+
+
+
+=== Environment
+
+The environment basically is also a kind of property/value provider similar to +System
+.getenv()+ in the JDK. Nevertheless it provides additional functionality:
+
+[source,java]
+.Interface Environment
+--------------------------------------------
+public interface Environment {
+
+ Optional<String> get(String key);
+ boolean containsKey(String key);
+ Set<String> keySet();
+ Map<String,String> toMap();
+
+ public static Environment current();
+ public static Environment root();
+--------------------------------------------
+
+* Basically an environment can contain any properties. The root environment
+ hereby must contain at least
+ ** all JDK's environment properties.
+ ** additional root properties are allowed as well.
+* the root environment is always directly accessible by calling +Environment.root()+
+* the current environment can be accessed by calling +Environment.current()+.
+
+Summarizing the Environment can be seen as a runtime context. This also implies, that this context changes
+depending on the current runtime context. Developers implementing an environment mechanism should be aware that
+an environment can be accessed very frequently, so evaluation and access of an +Environment+ must be fast. For
+further details we recommend the SPI details section of the core implementation.
+
+
+== SPI
+
+[[API PropertySourceBuilder]]
+==== Building Property Sources
+
+In [[PropertSource]] we have outlines that the essence of a property key store for configuration can be modelled by
+the +PropertySource+ interface. Similarly to the +ConfigurationBuilder+ you can also combine several +PropertySource+
+instances to assemble more complex configuration scenarios. Typically assembling is done within a +ConfigProvider+,
+which is responsible for providing correct Configuration corresponding to the current environment.
+
+Summarizing you can
+* aggregate providers, hereby later providers added
+ ** override any existing entries from earlier providers
+ ** combine conflicting entries from earlier providers, e.g. into a comma-separated structure.
+ ** may throw a ConfigException ig entries are conflicting
+ ** may only add entries not yet defined by former providers, preventing entries that are already present to be overwritten
+ ** any custom aggregation strategy, which may be a mix of above
+* intersecting providers
+* subtracting providers
+* filtering providers
+
+The following code snippet gives a couple of examples:
+
+[source,java]
+.Example Combining PropertySources
+--------------------------------------------
+PropertySource provider1 = ...
+PropertySource provider2 = ...
+
+// aggregate, hereby values from provider 2 override values from provider 1
+PropertySource unionOverriding = PropertySourceBuilder.of()
+ .aggregate(provider1, provider2).build(); // OVERRIDE policy is default
+System.out.println("unionOverriding: " + unionOverriding);
+
+// ignore duplicates, values present in provider 1 are not overriden by provider 2
+PropertySource unionIgnoringDuplicates = PropertySources
+ .aggregate(AggregationPolicy.IGNORE_DUPLICATES(), provider1, provider2).build();
+System.out.println("unionIgnoringDuplicates: " + unionIgnoringDuplicates);
+
+// this variant combines/maps duplicate values into a new value
+PropertySource unionCombined = PropertySourceBuilder.of().withAggregationPolicy(AggregationPolicy.COMBINE))
+ .aggregate(provider1, provider2).build();
+System.out.println("unionCombined: " + unionCombined);
+
+// This variant throws an exception since there are key/value paris in both providers, but with different values
+try{
+ PropertySourceBuilder.of().withAggregationPolicy(AggregationPolicy.EXCEPTION).aggregate(provider1, provider2);
+}
+catch(ConfigException e){
+ // expected!
+}
+--------------------------------------------
+
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/design/3_Extensions.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/design/3_Extensions.adoc b/docs/src/main/asciidoc/design/3_Extensions.adoc
deleted file mode 100644
index db949ac..0000000
--- a/docs/src/main/asciidoc/design/3_Extensions.adoc
+++ /dev/null
@@ -1,841 +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.
-<<<
-[[CoreConcepts]]
-== {name} Core Concepts
-Though {name} is a very powerful and flexible solution there are basically only a few simple core concepts required that build
-the base of all the other mechanisms:
-
-The API contains the following core concepts/artifacts:
-
-* Literal Key/Value Pairs
-* _PropertyProvider:_ is the the SPI for a source that provides configuration data. A +PropertyProvider+
- hereby defines
- ** a minimalistic SPI to be implemented by the config data source
- ** provides data key/value pairs in raw format as String key/values only
- ** providers should not have any dependencies other than to the datasource
- ** providers may read context dependent data, but basically providers themselves are not contextual.
- Context management should be done by the ConfigurationProvider implementation that also is responsible
- for combining a set of property providers to a Configuration.
- _Configuration_ is the API that users of Tamaya will see, when they access configuration in raw format. Hereby +Configuration+
- ** adds type support for non String types
- ** provides functional extension points (+with,query+)
- ** allows registering/deregistering of change listeners
- ** is the entry point for evaluating the current +Configuration+
- ** each +PropertyProvider+ can be easily converted into a +Configuration+
- ** allows configuration entries to be injected
- ** to access configuration _templates_ (annotated interfaces).
- ** Configuration may support mutability by allowing instances of +ConfigChangeSet+ to be passed.
-* _PropertyProviders_ allows to aggregate different property providers. Hereby property providers are
- seen as sets, which can be combined to new providers using set styled operations (aggregate, intersect, subtract).
- This allows to model and create composite container providers, to build up more complex configuration models
- step by step.
-* _MetaInfo_ is provided by each +Configuration, PropertyProvider+ and describes the configuration/provider and its entries.
-* _Environment_ is the base model for modelling the environment and the accessor for getting the current +Environment+ instance.
-* _Annotations_ a set of annotations allows to configure configuration injection on classes or interface (aka config templates).
-
-The SPI contains the following core concepts/artifacts:
-
-* _Bootstrap_ is the delegate singleton that is used by the framework to resolve components. The effective component
- loading can be accessed by implementing and registering an instance of +ServiceProvider+ using +java.util.ServiceLoader+.
-* All the singleton used explicitly (+PropertyAdapters,PropertyProviders+ are backed up corresponding API interfaces.
- To override a singleton's behaviour the corresponding SPI has to be implemented and registered, so it can be loaded
- by the current +Bootstrap+ setup (by default ServiceLoader based).
-* Also the singleton used implicitly by +Configuration, Environment, Stage+ are backed up corresponding SPI interfaces.
- To override a singleton's behaviour the corresponding SPI has to be implemented and registered, so it can be loaded
- by the current +Bootstrap+ setup (by default ServiceLoader based).
-
-This is also reflected in the main parts of the API, which is quite small:
-
-* +org.apache.tamaya+ contains the main abstractions +Configuration, ConfigOperator, ConfigQuery, PropertyAdapter, Stage,
- Environment, PropertyProvider, MetaInfo+
-* +org.apache.tamaya.spi+ contains the SPI interfaces to be implemented by implementations and the +Bootstrap+ mechanism.
-+ +org.apache.tamaya.annot+ contains the annotations defined.
-
-In the implementation are there are additional projects:
-
-* +org.apache.tamaya.core+ contains the core implementation of the API. Deploying it together with the API results in a
- flexible framework that can be easily used for configuration of different complexity. But out of the box this framework
- will not do much more than exposing system and environment properties, its power comes when an additional meta-model
- is defined and deployed. Hereby you can write your own, or use on e of the provided ones (see later).
-* the core part is extended by multiple additional modules
- ** CDI integration
- ** Default configuration meta-models and providers for the most common usage scenarios
- *** standalone applications
- *** Java EE
- *** ...
-
-These parts are explained in the following sections. It is recommended that user's of the API read through this part.
-All subsequent parts are building upon this concepts and may be more difficult to understand without having read
-this section.
-
-
-[[APIKeyValues]]
-=== Key/Value Pairs
-
-Basically configuration is a very generic concept. Therefore it should be modelled in a generic way. The most simple
-and similarly most commonly used are simple literal key/value pairs. So the core building block of {name} are key/value pairs.
-You can think of a common +.properties+ file, e.g.
-
-[source,properties]
-.A simple properties file
---------------------------------------------
-a.b.c=cVal
-a.b.c.1=cVal1
-a.b.c.2=cVal2
-a=aVal
-a.b=abVal
-a.b2=abVal
---------------------------------------------
-
-Now you can use +java.util.Properties+ to read this file and access the corresponding properties, e.g.
-
-[source,properties]
-.Accessing some properties
---------------------------------------------
-Properties props = new Properties();
-props.readProperties(...);
-String val = props.getProperty("a.b.c");
-val = props.getProperty("a.b.c.1");
-...
---------------------------------------------
-
-This looks familiar to most of you. Nevertheless when looking closer to the above key/value pairs,
-there are more concepts in place: looking at the keys +a.b.c+, +a.b.c.1+, +a.b.c.2+, +a+, +a.b+ we
-see that the key names build up a flattened tree structure. So we can define the following:
-
-Given a key +p1.p2.p3.k=value+:
-
-* +p1.p2.p3.k+ is called the _qualified key_
-* +p1.p2.p3+ is the key's _area_
-* the child areas +p1.p2", "p1+ are called _areas_ as well
-* +k+ is the _(unqualified) key_
-
-Given that you can perform some very useful actions:
-
-* you can filter the keys with an area. E.g. in the example before you can query for all keys within the area +a.b.c+
- and map them to new properties set as follows:
-
-[source,properties]
-.Accessing an area
---------------------------------------------
-1=cVal1
-2=cVal2
---------------------------------------------
-
-Similarly accessing the area +a+ results in the following properties:
-
-[source,properties]
-.Accessing the area +a+
---------------------------------------------
-b=abVal
-b2=abVal
---------------------------------------------
-
-Additionally you can access all values of an area recursively, so accessing +a+ recursively results in
-the following properties:
-
-[source,properties]
-.Accessing area +a+ recursively
---------------------------------------------
-b.c=cVal
-b.c.1=cVal1
-b.c.2=cVal2
-b=abVal
-b2=abVal
---------------------------------------------
-
-Why this is useful? Well there are different use cases:
-
-* you can segregate your configuration properties, e.g. a module can access its module configuration by
- querying all properties under the area +config.modules.myModule+ (or whatever would be appropriate).
-* you can use this mechanism to configure maps (or more generally: collections).
-* you can easily filter parts of configuration
-* ...and more.
-
-==== Why Using Strings Only
-
-Using Strings as base representation of configuration comes with several huge advantages:
-
-* Strings are simple to understand
-* Strings are human readable and therefore easy to prove for correctness
-* Strings can easily be used within different language, different VMs, files or network communications.
-* Strings can easily be compared and manipulated
-* Strings can easily be searched, indexed and cached
-* It is very easy to provide Strings as configuration, which gives much flexibility for providing configuration in
- production as well in testing.
-* and more
-
-On the other side there are also disadvantages:
-
-* Strings are inherently not type safe, they do not provide validation out of the box for special types, such as
-numbers,
- dates etc.
-* Often you want not to work with Strings, but with according types.
-* Strings are not hierarchical, so mapping hierarchical structures requires some extra efforts.
-
-Nevertheless most of these advantages can be mitigated easily, hereby still keeping all the benefits from above:
-
-* Adding type safe converters on top of String allow to add any type easily, that can be directly mapped out of Strings.
- This includes all common base types such as numbers, dates, time, but also timezones, formatting patterns and more.
-* Even more complex mappings can be easily realized, by using String not as a direct representation of configuration,
- but a reference that defines where the more complex configuration artifact is available. This mechanism is similarly
- easy to understand as parsing Strings to numbers, but is powerful enough to provide e.g. all kind of deployment
- descriptors in Java EE.
-* Hierarchical and collection types can be mapped in different ways:
-** The keys of configuration can have additional syntax/semantics. E.g. when adding dor-separating path semantics
-*** trees/maps can also simply be mapped.
-
-[APIPropertyProviders]
-=== Property Providers
-==== Basic Model
-
-We have seen that constrain configuration aspects to simple literal key/value pairs provides us with an easy to
-understand, generic, flexible, yet expendable mechanism. Looking at the Java language features a +vava.util.Map<String,
-String>+ and +java.util.Properties+ basically model these quite well out of the box.
-So it makes sense to build configuration on top of the JDK's +Map+ interface. This creates immediately additional
-benefits:
-
-* we inherit full Lambda and collection support
-* Maps are widely known and well understood
-
-Nevertheless there are some things to be considered:
-
-* Configuration also requires meta-data, such as
-** the origin of a certain configuration entry and how it was derived from other values
-** the sensitivity of some data
-** the provider that have read the data
-** the time, when the data was read
-** the timestamp, when some data may be outdated
-** ...
-
-Basically the same is also the not related to some single configuration key, but also to a whole map.
-The +PropertyProvider+ interface models exact these aspects and looks as illustrated below:
-
-[source,java]
-.Interface PropertyProvider
---------------------------------------------
-public interface PropertyProvider{
-
- Optional<String> get(String key);
- boolean containsKey(String key);
- Map<String, String> toMap();
- MetaInfo getMetaInfo();
-
- default Set<String> keySet();
- default ConfigChangeSet load();
- default boolean isMutable();
- default void apply(ConfigChangeSet change);
-}
---------------------------------------------
-
-Hereby
-
-* +getMetaInfo()+ return the meta information for the property provider, as well as for individual property key/value pairs.
-* +get, containsKey, keySet+ look similar to the methods on +Map+, though +get+ uses the +Optional+ type introduced
- with Java 8. This avoids returning +null+ or throwing exceptions in case no such entry is available and also
- reduced the API's footprint, since default values can be easily implemented by calling +Optional.orElse+.
-* +isMutable()+ allows to easy check, if a property provider is mutable, which is more elegant than catching
- +NonSupportedOperation+ exception thrown on the according methods of +Map+.
-* +load()+ finally allows to (re)load a property map. It depends on the implementing source, if this operation
- has any effect. If the map changes an according +ConfigChange+ must be returned, describing the
- changes applied.
-* +hasSameProperties+ allows to perform a comparison with another provider.
-* +toMap+ allows to extract thing to a +Map+.
-
-This simple model will be used within the spi, where configuration can be injected/provided from external resources.
-But we have seen, that we have to consider additional aspects, such as extendability and type safety. Therefore we
-extend +PropertyMap+ and hereby also apply the 'composite pattern', which results in the following key abstraction.
-
-==== Meta Information
-
-Each instance also provides an instance of +MetaInfo+, which provides meta information for the providers and its properties:
-
-[source,java]
-.Accessing Meta Information
---------------------------------------------
-PropertyProvider prov = ...;
-MetaInfo metaInfo = prov.getMetaInfo();
-Set<String> keys = metaInfo.keySet(); // returns the attribute keys, for which meta-information is accessible.
-String metaData = metaInfo.get("a.b.c.value"); // access meta information
-String itemName = metaInfo.getName(); // access meta information for the provider
---------------------------------------------
-
-As we have seen above there is as well a +MetaInfoBuilder+, which must be used to create instances of
-+MetaInfo+.
-
-==== Mutability
-
-Property providers optionally may be mutable. This can be checked by calling +boolean isMutable()+. If a provider
-is mutable a +ConfigChangeSet+ can be passed. This change set can then be applied by the provider. On creation
-of the +ConfigChangeSetBuilder+ a provider can pass version information, so _optimistic locking_ can be implemented
-easily:
-
-[source,java]
-.Creating and applying a +ConfigChangeSet+ to a provider
---------------------------------------------
-PropertyProvider prov = ...;
-ConfigChangeSet changeSet = ConfigChangeSetBuilder.of(provider) // creating a default version
- .remove("key1ToBeRemoved", +key2ToBeRemoved")
- .put("key2", "key2Value")
- .put("key3", 12345)
- .put("key4", 123.45)
- .build();
-provider.apply(changeSet);
---------------------------------------------
-
-[[API CombineProviders]]
-==== Combining Property Providers
-
-Looking at the structures of configuration system used by large companies we typically encounter some kind of configuration
-hierarchies that are combined in arbitrary ways. Users of the systems are typically not aware of the complexities in this
-area, since they simply know the possible locations, formats and the overriding policies. Framework providers on the other
-side must face the complexities and it would be very useful if Tamaya can support here by providing prebuilt functionality
-that helps implementing these aspects. All this leads to the feature set of combining property providers. Hereby the following
-strategies are useful:
-
-* aggregating providers, hereby later providers added
- ** override any existing entries from earlier providers
- ** combine conflicting entries from earlier providers, e.g. into a comma-separated structure.
- ** may throw a ConfigExcepotion ig entries are conflicting
- ** may only add entries not yet defined by former providers, preventing entries that are already present to be overwritte
- ** any custom aggregation strategy, which may be a mix of above
-* intersecting providers
-* subtracting providers
-* filtering providers
-
-These common functionality is provided by the +PropertyProviders+ singleton. Additionally to the base strategies above a +MetaInfo+
-instance can be passed optionally as well to define the meta information for the newly created provider instances.
-Let's assume we have two property providers with the following data:
-
-[source,properties]
-.Provider 1
---------------------------------------------
-a=a
-b=b
-c=c
-g=g
-h=h
-i=i
---------------------------------------------
-
-[source,properties]
-.Provider 2
---------------------------------------------
-a=A
-b=B
-c=C
-d=D
-e=E
-f=F
---------------------------------------------
-
-Looking in detail you see that the entries +a,b,c+ are present in both providers, whereas +d,e,f+ are only present in provider 1,
-and +g,h,i+ only in provider 2.
-
-[source,java]
-.Example Combining PropertyProviders
---------------------------------------------
-PropertyProvider provider1 = ...
-PropertyProvider provider2 = ...
-
-// aggregate, hereby values from provider 2 override values from provider 1
-PropertyProvider unionOverriding = PropertyProviders.aggregate(AggregationPolicy.OVERRIDE(), provider1, provider2);
-System.out.println("unionOverriding: " + unionOverriding);
-
-// ignore duplicates, values present in provider 1 are not overriden by provider 2
-PropertyProvider unionIgnoringDuplicates = PropertyProviders.aggregate(AggregationPolicy.IGNORE_DUPLICATES(), provider1, provider2);
-System.out.println("unionIgnoringDuplicates: " + unionIgnoringDuplicates);
-
-// this variant combines/maps duplicate values into a new value
-PropertyProvider unionCombined = PropertyProviders.aggregate(AggregationPolicy.COMBINE(), provider1, provider2);
-System.out.println("unionCombined: " + unionCombined);
-
-// This variant throws an exception since there are key/value paris in both providers, but with different values
-try{
- PropertyProviders.aggregate(AggregationPolicy.EXCEPTION(), provider1, provider2);
-}
-catch(ConfigException e){
- // expected!
-}
---------------------------------------------
-
-The example above produces the following outpout:
-
-[source,listing]
-.Example Combining PropertyProviders
---------------------------------------------
-AggregatedPropertyProvider{
- (name = dynamicAggregationTests)
- a = "[a][A]"
- b = "[b][B]"
- c = "[c][C]"
- d = "[D]"
- e = "[E]"
- f = "[F]"
- g = "[g]"
- h = "[h]"
- i = "[i]"
-}
-unionOverriding: AggregatedPropertyProvider{
- (name = <noname>)
- a = "A"
- b = "B"
- c = "C"
- d = "D"
- e = "E"
- f = "F"
- g = "g"
- h = "h"
- i = "i"
-}
-unionIgnoringDuplicates: AggregatedPropertyProvider{
- (name = <noname>)
- a = "a"
- b = "b"
- c = "c"
- d = "D"
- e = "E"
- f = "F"
- g = "g"
- h = "h"
- i = "i"
-}
-unionCombined: AggregatedPropertyProvider{
- (name = <noname>)
- a = "a,A"
- b = "b,B"
- c = "c,C"
- d = "D"
- e = "E"
- f = "F"
- g = "g"
- h = "h"
- i = "i"
-}
---------------------------------------------
-
-No +AggregationPolicy+ is also an interface that can be implemented:
-
-[source,java]
-.AggregationPolicy Interface
---------------------------------------------
-@FunctionalInterface
-public interface AggregationPolicy {
- String aggregate(String key, String value1, String value2);
-}
---------------------------------------------
-
-So we can also define our own aggregation strategy using a Lambda expression:
-
-[source,java]
-.Use a Custom AggregationPolicy
---------------------------------------------
-PropertyProvider provider1 = ...;
-PropertyProvider provider2 = ...;
-PropertyProvider props = PropertyProviders.aggregate(
- (k, v1, v2) -> (v1 != null ? v1 : "") + '[' + v2 + "]",
- MetaInfo.of("dynamicAggregationTests"),
- props1, props2);
-System.out.println(props);
---------------------------------------------
-
-Additionally we also pass here an instance of +MetaInfo+. The output of this code snippet is as follows:
-
-[source,listing]
-.Listing of dynamic aggregation policy
---------------------------------------------
-AggregatedPropertyProvider{
- (name = dynamicAggregationTests)
- a = "[a][A]"
- b = "[b][B]"
- c = "[c][C]"
- d = "[D]"
- e = "[E]"
- f = "[F]"
- g = "[g]"
- h = "[h]"
- i = "[i]"
-}
---------------------------------------------
-
-Summarizing the +PropertyProviders+ singleton allows to combine providers in various forms:
-
-[source,listing]
-.Methods provided on PropertyProviders
---------------------------------------------
-public final class PropertyProviders {
-
- private PropertyProviders() {}
-
- public static PropertyProvider fromArgs(String... args) {
- public static PropertyProvider fromArgs(MetaInfo metaInfo, String... args) {
- public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, String... paths) {
- public static PropertyProvider fromPaths(String... paths) {
- public static PropertyProvider fromPaths(List<String> paths) {
- public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, List<String> paths) {
- public static PropertyProvider fromPaths(MetaInfo metaInfo, List<String> paths) {
- public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<String> paths) {
- public static PropertyProvider fromUris(URI... uris) {
- public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, URI... uris) {
- public static PropertyProvider fromUris(List<URI> uris) {
- public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, List<URI> uris) {
- public static PropertyProvider fromUris(MetaInfo metaInfo, URI... uris) {
- public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, URI... uris) {
- public static PropertyProvider fromUris(MetaInfo metaInfo, List<URI> uris) {
- public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<URI> uris) {
- public static PropertyProvider fromMap(Map<String, String> map) {
- public static PropertyProvider fromMap(MetaInfo metaInfo, Map<String, String> map) {
- public static PropertyProvider empty() {
- public static PropertyProvider emptyMutable() {
- public static PropertyProvider empty(MetaInfo metaInfo) {
- public static PropertyProvider emptyMutable(MetaInfo metaInfo) {
- public static PropertyProvider fromEnvironmentProperties() {
- public static PropertyProvider fromSystemProperties() {
- public static PropertyProvider freezed(PropertyProvider provider) {
- public static PropertyProvider aggregate(AggregationPolicy mapping, MetaInfo metaInfo, PropertyProvider... providers){
- public static PropertyProvider aggregate(PropertyProvider... providers) {
- public static PropertyProvider aggregate(List<PropertyProvider> providers) {
- public static PropertyProvider aggregate(AggregationPolicy mapping, PropertyProvider... propertyMaps) {
- public static PropertyProvider aggregate(AggregationPolicy mapping, List<PropertyProvider> providers) {
- public static PropertyProvider mutable(PropertyProvider provider) {
- public static PropertyProvider intersected(AggregationPolicy aggregationPolicy, PropertyProvider... providers) {
- public static PropertyProvider intersected(PropertyProvider... providers) {
- public static PropertyProvider subtracted(PropertyProvider target, PropertyProvider... providers) {
- public static PropertyProvider filtered(Predicate<String> filter, PropertyProvider provider) {
- public static PropertyProvider contextual(Supplier<PropertyProvider> mapSupplier,
- Supplier<String> isolationKeySupplier) {
- public static PropertyProvider delegating(PropertyProvider mainMap, Map<String, String> parentMap) {
- public static PropertyProvider replacing(PropertyProvider mainMap, Map<String, String> replacementMap) {
-}
---------------------------------------------
-
-
-[[API Configuration]]
-=== Configuration
-==== Basic Model
-
-Configuration inherits all basic features from +PropertyProvider+, but additionally adds functionality for
-type safety and extension mechanisms:
-
-[source,java]
-.Interface Configuration
---------------------------------------------
-public interface Configuration extends PropertyProvider{
-
- default OptionalBoolean getBoolean(String key);
- default OptionalInt getInteger(String key);
- default OptionalLong getLong(String key);
- default OptionalDouble getDouble(String key);
- default <T> Optional<T> getAdapted(String key, PropertyAdapter<T> adapter);
- <T> Optional<T> get(String key, Class<T> type);
-
- // accessing areas
- default Set<String> getAreas();
- default Set<String> getTransitiveAreas();
- default Set<String> getAreas(final Predicate<String> predicate);
- default Set<String> getTransitiveAreas(Predicate<String> predicate);
- default boolean containsArea(String key);
-
- // extension points
- default Configuration with(ConfigOperator operator);
- default <T> T query(ConfigQuery<T> query);
-
- // versioning
- default String getVersion(){return "N/A";}
- void addPropertyChangeListener(PropertyChangeListener l);
- void removePropertyChangeListener(PropertyChangeListener l);
-
- // singleton accessors
- public static boolean isDefined(String name);
- public static <T> T current(String name, Class<T> template);
- public static Configuration current(String name);
- public static Configuration current();
- public static <T> T current(Class<T> type){
- public static void configure(Object instance);
- public static String evaluateValue(String expression);
- public static String evaluateValue(Configuration config, String expression);
- public static void addGlobalPropertyChangeListener(PropertyChangeListener listener);
- public static void removeGlobalPropertyChangeListener(PropertyChangeListener listener);
-}
---------------------------------------------
-
-Hereby
-
-* +XXX getXXX(String)+ provide type safe accessors for all basic wrapper types of the JDK.
-* +getAdapted+ allow accessing any type, hereby also passing a +PropertyAdapter+ that converts
- the configured literal value to the type required.
-* +getAreas()+, +getTransitiveAreas()+ allow to examine the hierarchical tree modeled by the configuration tree.
- Optionally also predicates can be passed to select only part of the tree to be returned.
-* +containsArea+ allows to check, if an area is defined.
-* +with, query+ provide the extension points for adding additional functionality.
-
-* the static accessor methods define:
- ** +current(), current(Class), current(String), current(String, Class)+ return the configuration valid for the current runtime environment.
- ** +addPropertyChangeListener, removePropertyChangeListener+ allow to register or unregister
- global config change listener instances.
- ** evaluateValue allows to evaluate a configuration expression based on a given configuration.
- ** +configure+ performs injection of configured values.
-
-[[TypeConversion]]
-==== Type Conversion
-
-Configuration extend +PropertyProvider+ and add additional support for non String types. This is achieved
-with the help of +PropertyAdapter+ instances:
-
-[source,java]
-.PropertyAdapter
---------------------------------------------
-@FunctionalInterface
-public interface PropertyAdapter<T>{
- T adapt(String value);
-}
---------------------------------------------
-
-PropertyAdapter instances can be implemented manually or registered and accessed from the
-+PropertyAdapers+ singleton. Hereby the exact mechanism is determined by the API backing up the singleton.
-By default corresponding +PropertyAdapter+ instances can be registered using the Java +ServiceLoader+
-mechanism, or programmatically ba calling the +register(Class, PropertyAdapter)+ method.
-
-[source,java]
---------------------------------------------
-public final class PropertyAdapters{
- public static <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> adapter);
- public static boolean isTargetTypeSupported(Class<?> targetType);
- public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType);
- public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType, WithPropertyAdapter annotation);
-}
---------------------------------------------
-
-Whereas this mechanism per se looks not very useful it's power shows up when combining it with the annotations
-API provided, e.g. look at the following annotated class:
-
-[source,java]
-.Annotated Example Class
---------------------------------------------
-public class ConfiguredClass{
-
- @ConfiguredProperty
- private String testProperty;
-
- @ConfiguredProperty("a.b.c.key1")
- @DefaultValue("The current \\${JAVA_HOME} env property is ${env:JAVA_HOME}.")
- String value1;
-
- @ConfiguredProperty("a.b.c.key2")
- private int value2;
-
- @ConfiguredProperty
- @DefaultValue("http://127.0.0.1:8080/res/api/v1/info.json")
- private URL accessUrl;
-
- @ConfiguredProperty
- @DefaultValue("5")
- private Integer int1;
-
- @ConfiguredProperty("a.b.customType")
- private MyCustomType myCustomType;
-
- @ConfiguredProperty("BD")
- private BigDecimal bigNumber;
-
- ...
-}
---------------------------------------------
-
-The class does not show all the possibilities that are provided, but it shows that arbitrary types can be supported easily.
-This applied similarly to collection types, whereas collections are more advanced and therefore described in a separate section
-later.
-
-Given the class above and the current configuration can provide the values required, configuring an instance of the
-class is simple:
-
-[source,java]
-.Configuring the Example Class
---------------------------------------------
-ConfiguredClass classInstance = new ConfiguredClass();
-Configuration.configure(configuredClass);
---------------------------------------------
-
-Additional types can transparently be supported by implementing and registering corresponding SPI instances. This is explained
-in the SPI documentation of {name}.
-
-==== Extension Points
-
-We are well aware of the fact that this library will not be able to cover all kinds of use cases. Therefore
-we have added similar functional extension mechanisms that were used in other areas of the Java eco-system as well:
-
-* +ConfigOperator+ define unary operations on +Configuration+. They can be used for filtering, implementing
- configuration views, security interception etc.
-* +ConfigQuery+ defines a function returning any kind of result based on a configuration instance. Typical
- use cases of queries could be the implementation of configuration SPI instances that are required
- by other libraries or frameworks.
-
-Both interfaces hereby are defined as functional interfaces:
-
-[source,java]
-.ConfigOperator and ConfigQuery
---------------------------------------------
-@FunctionalInterface
-public interface ConfigOperator{
- Configuration operate(Configuration config);
-}
-
-@FunctionalInterface
-public interface ConfigQuery<T>{
- T query(Configuration config);
-}
---------------------------------------------
-
-Both interfaces can be applied on a +Configuration+ instance:
-
-[source,java]
-.Applying Config operators and queries
---------------------------------------------
-Configuration secured = Configuration.of().apply(ConfigSecurity::secure);
-ConfigSecurity securityContext = Configuration.of().query(ConfigSecurity::targetSecurityContext);
---------------------------------------------
-
-NOTE: +ConfigSecurity+ is an arbitrary class.
-
-=== Configuration Injection
-
-The +Configuration+ interface provides static methods that allow to anykind of instances be configured
-ny just passing the instances calling +Configuration.configure(instance);+. The classes passed hereby must
-be annotated with +@ConfiguredProperty+ to define the configured properties. Hereby this annotation can be
-used in multiple ways and combined with other annotations such as +@DefaultValue+,
-+@WithLoadPolicy+, +@WithConfig+, +@WithConfigOperator+, +@WithPropertyAdapter+.
-
-To illustrate the mechanism below the most simple variant of a configured class is given:
-
-[source,java]
-.Most simple configured class
---------------------------------------------
-pubic class ConfiguredItem{
- @ConfiguredProperty
- private String aValue;
-}
---------------------------------------------
-
-When this class is configured, e.g. by passing it to +Configuration.configure(Object)+,
-the following is happening:
-
-* The current valid +Configuration+ is evaluated by calling +Configuration cfg = Configuration.of();+
-* The current property value (String) is evaluated by calling +cfg.get("aValue");+
-* if not successful, an error is thrown (+ConfigException+)
-* On success, since no type conversion is involved, the value is injected.
-* The configured bean is registered as a weak change listener in the config system's underlying
- configuration, so future config changes can be propagated (controllable by applying the
- +@WithLoadPolicy+ annotation).
-
-In the next example we explicitly define the property value:
-[source,java]
---------------------------------------------
-pubic class ConfiguredItem{
-
- @ConfiguredProperty
- @ConfiguredProperty("a.b.value")
- @configuredProperty("a.b.deprecated.value")
- @DefaultValue("${env:java.version}")
- private String aValue;
-}
---------------------------------------------
-
-Within this example we evaluate multiple possible keys. Evaluation is aborted if a key could be successfully
-resolved. Hereby the ordering of the annotations define the ordering of resolution, so in the example above
-resolution equals to +"aValue", "a.b.value", "a.b.deprecated.value"+. If no value could be read
-from the configuration, it uses the value from the +@DefaultValue+ annotation. Interesting here
-is that this value is not static, it is evaluated by calling +Configuration.evaluateValue(Configuration, String)+.
-
-=== Environment
-
-The environment basically is also a kind of property/value provider similar to +System.getProperties()+ and +System
-.getenv()+ in the JDK. Nevertheless it provides additional functionality:
-
-[source,java]
-.Interface Environment
---------------------------------------------
-public interface Environments {
-
- String getEnvironmentType();
- String getEnvironmentId();
- Environment getParentEnvironment();
-
- Optional<String> get(String key);
- boolean containsKey(String key);
- Set<String> keySet();
- Map<String,String> toMap();
-
- public static Environment current(){
- public static Environment getRootEnvironment(){
- public static List<String> getEnvironmentTypeOrder(){
- public static List<String> getEnvironmentHierarchy(){
- public static Optional<Environment> getInstance(String environmentType, String contextId){
- public static Set<String> getEnvironmentContexts(String environmentType){
- public static boolean isEnvironmentActive(String environmentType){
---------------------------------------------
-
-* environments are hierarchical. Hereby all environments inherit from the root environment. The root environment
- hereby must contain
- ** all JDK's system properties, with same keys, values
- ** all JDK's environment properties, prefixed with +env:+.
- ** additional root properties are allowed as well.
-* the root environment is always directly accessible by calling +Environment.getRootEnvironment()+
-* the current environment can be accessed by calling +Environment.of()+.
-* each environment also defines a +Stage+ (implementing +StageSupplier+). Hereby, if not set explicitly the +Stage+ is inherited from the root
- environment. Consequently the root environment must provide a +Stage+, which by default is +Stage.development()+.
-
-Additionally each environment instance is uniquely identified by the environment type (accessible from
-+getEnvironmentType()+ and the environment id (accessible from +getEnvironmentId()+). So it is possible to access
-an +Environment+ by calling +of(String environmentType, String environmentId)+. Implementations may restrict access
-to environments depending on the current runtime environment (runtime context) active. The API does
-not require further aspects.
-
-The call to +getEnvironmentIds(String)+ returns all context ids of the known +Environment+ instances
-of a given type. E.g. assuming there is an environment type +war+ calling +Environment.getEnvironmentIds("war")+
-may return +"/web/app1", "/web/app2"+ (assuming the war context ids equal the web applications root contexts).
-
-All environments are basically ordered. The ordering can be accessed by calling +getEnvironmentTypeOrder()+. Hereby
-not every environment type in a hierarchy must necessarily present. This is reflected by +getEnvironmentHierarchy()+
-which returns the environment type ids in order, but only containing the types of the environments
-currently present and accessible in the hierarchy. As an example an environment type order in an advanced
-use case could be something like +"root","ear","war","saas","user"+, whereas the concrete environment type hierarchy
-may be +"root","war","saas"+, because the application was not included
-in an additional ear archive and no user is currently active (anonymous). The call to +isEnvironmentActive(String)+
-allows to determine if an environment of the given type is currently active.
-Finally the environment hierarchy is of course similarly reflected by the relationship (+getParentEnvironment()+).
-The following code should illustrate some of these concepts:
-
-[source,java]
-.Interface Environment
---------------------------------------------
-List<String> envHierarchy = Environment.getEnvironmentHierarchy();
- // -> "root","war","saas"
-Environment env = Environment.of();
-System.out.println(env.getEnvironmentContext()); // saas
-System.out.println(env.getEnvironmentId()); // mysolution_pro
-env = env.getParentEnvironment();
-System.out.println(env.getEnvironmentContext()); // war
-System.out.println(env.getEnvironmentId()); // pro
-env = env.getParentEnvironment();
-System.out.println(env.getEnvironmentContext()); // root
-System.out.println(env.getEnvironmentId()); // system
-env = env.getParentEnvironment();
-// env is null now!
---------------------------------------------
-
-
[4/4] incubator-tamaya git commit: TAMAYA-19: Streamlined API and
impl.
Posted by an...@apache.org.
TAMAYA-19: Streamlined API and impl.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/a60570e8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/a60570e8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/a60570e8
Branch: refs/heads/master
Commit: a60570e8e9e561dd2c02c7d0978e846d4d9dd27e
Parents: 0b5d4fe
Author: anatole <an...@apache.org>
Authored: Fri Dec 26 01:49:55 2014 +0100
Committer: anatole <an...@apache.org>
Committed: Fri Dec 26 01:49:55 2014 +0100
----------------------------------------------------------------------
.../org/apache/tamaya/AggregationPolicy.java | 131 ---
.../java/org/apache/tamaya/Configuration.java | 37 +-
.../java/org/apache/tamaya/PropertySource.java | 45 -
.../tamaya/annotation/ConfiguredProperty.java | 2 +-
.../org/apache/tamaya/annotation/WithCodec.java | 45 -
.../tamaya/annotation/WithConfigOperator.java | 2 +-
.../tamaya/annotation/WithPropertyAdapter.java | 45 +
.../java/org/apache/tamaya/spi/CodecSpi.java | 89 --
.../tamaya/spi/DefaultServiceComparator.java | 85 --
.../spi/DefaultServiceContextProvider.java | 9 -
.../java/org/apache/tamaya/spi/Orderable.java | 34 -
.../org/apache/tamaya/spi/OrdinalProvider.java | 37 -
.../apache/tamaya/spi/PropertyAdapterSpi.java | 72 ++
.../tamaya/TestConfigServiceSingletonSpi.java | 15 -
.../TestPropertyAdaptersSingletonSpi.java | 66 +-
.../services/org.apache.tamaya.spi.CodecSpi | 19 -
.../org.apache.tamaya.spi.PropertyAdapterSpi | 19 +
.../core/config/FreezedConfiguration.java | 4 +-
.../core/internal/config/DefaultCodecSpi.java | 169 ----
.../config/DefaultPropertyAdapterSpi.java | 168 ++++
.../config/FallbackSimpleConfigProvider.java | 2 +-
.../core/internal/config/FileConfiguration.java | 2 -
.../internal/resources/io/AntPathMatcher.java | 4 +-
.../AbstractClasspathAwarePropertySource.java | 3 -
.../core/properties/AbstractPropertySource.java | 3 +-
.../core/properties/AggregationPolicy.java | 133 +++
.../properties/BuildablePropertySource.java | 2 -
.../properties/EnvironmentPropertySource.java | 3 -
.../core/properties/URLBasedPropertySource.java | 1 -
.../tamaya/core/spi/CodecProviderSpi.java | 36 -
.../core/spi/DefaultServiceComparator.java | 85 ++
.../core/spi/DefaultServiceContextProvider.java | 112 +++
.../org/apache/tamaya/core/spi/Orderable.java | 34 +
.../apache/tamaya/core/spi/OrdinalProvider.java | 37 +
.../core/spi/PropertyAdapterProviderSpi.java | 36 +
.../services/org.apache.tamaya.spi.CodecSpi | 19 -
.../org.apache.tamaya.spi.PropertyAdapterSpi | 19 +
.../apache/tamaya/ucs/UC1ReadProperties.java | 5 +-
.../apache/tamaya/ucs/UC2CombineProperties.java | 5 +-
docs/src/main/asciidoc/design/2_API.adoc | 469 ++++++++++
.../main/asciidoc/design/2_CoreConcepts.adoc | 849 -------------------
docs/src/main/asciidoc/design/3_Core.adoc | 356 ++++++++
docs/src/main/asciidoc/design/3_Extensions.adoc | 841 ------------------
docs/src/main/asciidoc/design/4_Extensions.adoc | 841 ++++++++++++++++++
.../asciidoc/usecases/se/combine-configs.adoc | 14 +
.../se/context-dependent-configuration.adoc | 7 +
.../usecases/se/dynamic-provisioning.adoc | 17 +
.../usecases/se/external-configuration.adoc | 6 +
docs/src/main/asciidoc/usecases/se/formats.adoc | 7 +
.../main/asciidoc/usecases/se/injection.adoc | 31 +
docs/src/main/asciidoc/usecases/se/java8.adoc | 13 +
.../main/asciidoc/usecases/se/locations.adoc | 9 +
.../main/asciidoc/usecases/se/management.adoc | 7 +
.../usecases/se/minimal-propertysource.adoc | 6 +
.../usecases/se/multiple-configurations.adoc | 14 +
.../usecases/se/scannable-properties.adoc | 4 +
.../asciidoc/usecases/se/service-context.adoc | 14 +
.../asciidoc/usecases/se/simple-access.adoc | 18 +
.../usecases/se/simple-property-access.adoc | 7 +
.../main/asciidoc/usecases/se/templates.adoc | 11 +
.../usecases/se/type-safe-properties.adoc | 10 +
.../usecases/se/value-placeholders.adoc | 8 +
docs/src/main/asciidoc/usecases/usecases.adoc | 19 +
63 files changed, 2695 insertions(+), 2517 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/AggregationPolicy.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/AggregationPolicy.java b/api/src/main/java/org/apache/tamaya/AggregationPolicy.java
deleted file mode 100644
index c0f7cc0..0000000
--- a/api/src/main/java/org/apache/tamaya/AggregationPolicy.java
+++ /dev/null
@@ -1,131 +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;
-
-import java.util.logging.Logger;
-
-/**
- * Policy that defines how the different configurations/property sources should be aggregated.
- * This is done by a mapping function defined as follows:
- * <pre>
- * function f(key, value1, value2) -> result
- *
- * whereas
- *
- * key = the fully qualified property key,
- * value1 = the value from the first configuration/property source (can be null)
- * value2 = the value from the second configuration/property source (can be null)
- *
- * result = the value to be used in the aggregation, or null, which removed the
- * key from the result.
- * </pre>
- *
- * Of course, during this evaluation step additional actions can be taken, e.g. refer to #LOG_ERROR, which
- * ignores duplicate entries, but also logs the conflict on severe/error level.
- */
-public interface AggregationPolicy {
-
- /**
- * Method which decides how keys/values are aggregated.
- * @param key the key current the entry, must not be {@code null}.
- * @param currentValue the current keys, or {@code null}.
- * @param newValue the new keys, never {@code null}.
- * @return the target keys to be used in the resulting property set, or null, to remove the property.
- */
- public String aggregate(String key, String currentValue, String newValue);
-
- /** Ignore overrides, only extend (additive). */
- public static final AggregationPolicy IGNORE_DUPLICATES = (k, v1, v2) -> v1 == null? v2 : v1;
-
- /** Combine multiple values into a comma separated list. */
- public static final AggregationPolicy COMBINE = (k, v1, v2) -> v1 != null && v2 != null ? v1 + ',' + v2: v2;
-
- /**
- * Interpret later keys as override (additive and override), replacing
- * the key loaded earlier/fromMap previous contained
- * {@link PropertySource}.
- */
- public static final AggregationPolicy OVERRIDE = (k, v1, v2) -> v2;
-
- /**
- * Throw an exception, when keys are not disjunctive (strictly
- * additive).
- */
- public static final AggregationPolicy EXCEPTION =
- (String key, String value, String newValue) -> {
- if(value!=null && newValue!=null && !value.equals(newValue)){
- throw new ConfigException("Conflicting values encountered key="+key+", keys="+value+", newValue="+newValue);
- }
- return newValue;
- };
-
- /**
- * Ignores any duplicates, but logs the conflict encountered to error/severe level.
- */
- public static final AggregationPolicy LOG_ERROR =
- (String key, String value, String newValue) -> {
- if(value!=null && newValue!=null && !value.equals(newValue)){
- Logger.getLogger(AggregationPolicy.class.getName())
- .severe(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
- return value;
- }
- return newValue;
- };
-
- /**
- * Ignores any duplicates, but logs the conflict encountered to info level.
- */
- public static final AggregationPolicy LOG_WARNING =
- (String key, String value, String newValue) -> {
- if(value!=null && newValue!=null && !value.equals(newValue)){
- Logger.getLogger(AggregationPolicy.class.getName())
- .warning(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
- return value;
- }
- return newValue;
- };
-
- /**
- * Ignores any duplicates, but logs the conflict encountered to info level.
- */
- public static final AggregationPolicy LOG_INFO =
- (String key, String value, String newValue) -> {
- if(value!=null && newValue!=null && !value.equals(newValue)){
- Logger.getLogger(AggregationPolicy.class.getName())
- .info(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
- return value;
- }
- return newValue;
- };
-
- /**
- * Ignores any duplicates, but logs the conflict encountered to debug/finest level.
- */
- public static final AggregationPolicy LOG_DEBUG =
- (String key, String value, String newValue) -> {
- if(value!=null && newValue!=null && !value.equals(newValue)){
- Logger.getLogger(AggregationPolicy.class.getName())
- .finest(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
- return value;
- }
- return newValue;
- };
-
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/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
index 543a515..8254e3a 100644
--- a/api/src/main/java/org/apache/tamaya/Configuration.java
+++ b/api/src/main/java/org/apache/tamaya/Configuration.java
@@ -22,7 +22,6 @@ import org.apache.tamaya.spi.ConfigurationSpi;
import org.apache.tamaya.spi.ServiceContext;
import java.util.*;
-import java.util.function.Consumer;
import java.util.function.UnaryOperator;
/**
@@ -126,10 +125,10 @@ public interface Configuration extends PropertySource {
* @throws IllegalArgumentException if the keys could not be converted to the required target
* type, or no such property exists.
*/
- default <T> Optional<T> getAdapted(String key, Codec<T> adapter){
+ default <T> Optional<T> getAdapted(String key, PropertyAdapter<T> adapter){
Optional<String> value = get(key);
if(value.isPresent()) {
- return Optional.ofNullable(adapter.deserialize(value.get()));
+ return Optional.ofNullable(adapter.adapt(value.get()));
}
return Optional.empty();
}
@@ -137,7 +136,7 @@ public interface Configuration extends PropertySource {
/**
* Get the property keys as type T. This will implicitly require a corresponding {@link
- * Codec} to be available that is capable current providing type T
+ * PropertyAdapter} to be available that is capable current providing type T
* fromMap the given String keys.
*
* @param key the property's absolute, or relative path, e.g. @code
@@ -148,7 +147,7 @@ public interface Configuration extends PropertySource {
* type.
*/
default <T> Optional<T> get(String key, Class<T> type){
- return getAdapted(key, Codec.getInstance(type));
+ return getAdapted(key, PropertyAdapter.getInstance(type));
}
/**
@@ -227,7 +226,6 @@ public interface Configuration extends PropertySource {
* @param configurations overriding configurations to be used for evaluating the values for injection into {@code instance}, not null.
* If no such config is passed, the default configurationa provided by the current
* registered providers are used.
- * @return the corresponding typed Configuration instance, never null.
* @throws ConfigException if the configuration could not be resolved.
*/
public static void configure(Object instance, Configuration... configurations){
@@ -247,31 +245,4 @@ public interface Configuration extends PropertySource {
return ServiceContext.getInstance().getSingleton(ConfigurationSpi.class).evaluateValue(expression, configurations);
}
- /**
- * Add a ConfigChangeListener to the given PropertySource instance.
- * @param l the listener, not null.
- */
- public static void addChangeListener(Consumer<ConfigChangeSet> l){
- ServiceContext.getInstance().getSingleton(ConfigurationSpi.class).addChangeListener(l);
- }
-
- /**
- * Removes a ConfigChangeListener from the given PropertySource instance.
- * @param l the listener, not null.
- */
- public static void removeChangeListener(Consumer<ConfigChangeSet> l){
- ServiceContext.getInstance().getSingleton(ConfigurationSpi.class).removeChangeListener(l);
- }
-
- /**
- * Method to publish changes on a {@link org.apache.tamaya.PropertySource} to all interested parties.
- * Basically this method gives an abstraction on the effective event bus design fo listeners. In a CDI context
- * the CDI enterprise event bus should be used internally to do the work, whereas in a SE only environment
- * a more puristic approach would be useful.
- * @param configChange the change to be published, not null.
- */
- public static void publishChange(ConfigChangeSet configChange){
- ServiceContext.getInstance().getSingleton(ConfigurationSpi.class).publishChange(configChange);
- }
-
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/PropertySource.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertySource.java b/api/src/main/java/org/apache/tamaya/PropertySource.java
index 2cd3af7..d0c5bc1 100644
--- a/api/src/main/java/org/apache/tamaya/PropertySource.java
+++ b/api/src/main/java/org/apache/tamaya/PropertySource.java
@@ -111,36 +111,6 @@ public interface PropertySource {
}
/**
- * Reloads the {@link PropertySource}.
- */
- default ConfigChangeSet load() {
- // by default do nothing
- return ConfigChangeSet.emptyChangeSet(this);
- }
-
- /**
- * Allows to evaluate if the provider is mutable.
- *
- * @return true, if the provider is mutable.
- * @see #applyChanges(ConfigChangeSet)
- */
- default boolean isMutable() {
- return false;
- }
-
- /**
- * Apply a config change to this item. Hereby the change must be related to the same instance.
- *
- * @param change the config change
- * @throws ConfigException if an unrelated change was passed.
- * @throws UnsupportedOperationException when the configuration is not writable.
- * @see #isMutable()
- */
- default void applyChanges(ConfigChangeSet change) {
- throw new UnsupportedOperationException("Config/properties not mutable: " + this);
- }
-
- /**
* Convert the this PropertyProvider instance to a {@link org.apache.tamaya.Configuration}.
*
* @return the configuration, never null.
@@ -158,26 +128,11 @@ public interface PropertySource {
}
@Override
- public boolean isMutable() {
- return PropertySource.this.isMutable();
- }
-
- @Override
- public void applyChanges(ConfigChangeSet changes) {
- PropertySource.this.applyChanges(changes);
- }
-
- @Override
public boolean isEmpty() {
return PropertySource.this.isEmpty();
}
@Override
- public ConfigChangeSet load() {
- return PropertySource.this.load();
- }
-
- @Override
public Optional<String> get(String key) {
return PropertySource.this.get(key);
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/annotation/ConfiguredProperty.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/annotation/ConfiguredProperty.java b/api/src/main/java/org/apache/tamaya/annotation/ConfiguredProperty.java
index a29ef8b..21d4e3a 100644
--- a/api/src/main/java/org/apache/tamaya/annotation/ConfiguredProperty.java
+++ b/api/src/main/java/org/apache/tamaya/annotation/ConfiguredProperty.java
@@ -25,7 +25,7 @@ import java.lang.annotation.*;
* a configuration template method. Hereby this annotation can be used in multiple ways and combined
* with other annotations such as {@link org.apache.tamaya.annotation.DefaultValue},
* {@link org.apache.tamaya.annotation.WithLoadPolicy}, {@link org.apache.tamaya.annotation.WithConfig},
- * {@link org.apache.tamaya.annotation.WithConfigOperator}, {@link WithCodec}.
+ * {@link org.apache.tamaya.annotation.WithConfigOperator}, {@link WithPropertyAdapter}.
*
* Below the most simple variant current a configured class is given:
* {@code
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/annotation/WithCodec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/annotation/WithCodec.java b/api/src/main/java/org/apache/tamaya/annotation/WithCodec.java
deleted file mode 100644
index 465d7b3..0000000
--- a/api/src/main/java/org/apache/tamaya/annotation/WithCodec.java
+++ /dev/null
@@ -1,45 +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.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import org.apache.tamaya.Codec;
-
-/**
- * Annotation to define a type adapter to be used before injecting a configured keys, or for applying changes.
- * This will override any other adapter for performing the type conversion before
- * injecting the field keys.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(value = { ElementType.FIELD, ElementType.METHOD })
-public @interface WithCodec {
-
- /**
- * Define a custom adapter or codec that should be used to deserialize the configuration entry injected. This overrides any
- * general org.apache.tamaya.core.internal registered. If no adapter is defined (default) and no corresponding adapter is
- * registered, it is handled as a deployment error.
- */
- @SuppressWarnings("rawtypes")
- Class<? extends Codec> value();
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/annotation/WithConfigOperator.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/annotation/WithConfigOperator.java b/api/src/main/java/org/apache/tamaya/annotation/WithConfigOperator.java
index 0ede2c2..9f6c4f5 100644
--- a/api/src/main/java/org/apache/tamaya/annotation/WithConfigOperator.java
+++ b/api/src/main/java/org/apache/tamaya/annotation/WithConfigOperator.java
@@ -36,7 +36,7 @@ import java.util.function.UnaryOperator;
public @interface WithConfigOperator {
/**
- * Define a custom adapter that should be used to deserialize the configuration entry injected. This overrides any
+ * Define a custom adapter that should be used to adapt the configuration entry injected. This overrides any
* general org.apache.tamaya.core.internal registered. If no adapter is defined (default) and no corresponding adapter is
* registered, it is handled as a deployment error.
*/
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/annotation/WithPropertyAdapter.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/annotation/WithPropertyAdapter.java b/api/src/main/java/org/apache/tamaya/annotation/WithPropertyAdapter.java
new file mode 100644
index 0000000..fa9cfdf
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/annotation/WithPropertyAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.annotation;
+
+import org.apache.tamaya.PropertyAdapter;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to define a type adapter to be used before injecting a configured keys, or for applying changes.
+ * This will override any other adapter for performing the type conversion before
+ * injecting the field keys.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.FIELD, ElementType.METHOD })
+public @interface WithPropertyAdapter {
+
+ /**
+ * Define a custom adapter or codec that should be used to adapt the configuration entry injected. This overrides any
+ * general org.apache.tamaya.core.internal registered. If no adapter is defined (default) and no corresponding adapter is
+ * registered, it is handled as a deployment error.
+ */
+ @SuppressWarnings("rawtypes")
+ Class<? extends PropertyAdapter> value();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/spi/CodecSpi.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/CodecSpi.java b/api/src/main/java/org/apache/tamaya/spi/CodecSpi.java
deleted file mode 100644
index 7a0670b..0000000
--- a/api/src/main/java/org/apache/tamaya/spi/CodecSpi.java
+++ /dev/null
@@ -1,89 +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.spi;
-
-import org.apache.tamaya.Codec;
-import org.apache.tamaya.annotation.WithCodec;
-
-import java.util.Objects;
-import java.util.function.Function;
-
-/**
- * SPI that is used by the {@link org.apache.tamaya.Codecs} singleton as delegation instance.
- */
-public interface CodecSpi {
-
- /**
- * 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.
- */
- <T> Codec<T> register(Class<T> targetType, Codec<T> adapter);
-
- default <T> Codec<T> register(Class<T> targetType, Function<String,T> decoder, Function<T, String> encoder){
- Objects.requireNonNull(targetType);
- Objects.requireNonNull(decoder);
- Objects.requireNonNull(encoder);
- return register(targetType, new Codec<T>(){
-
- @Override
- public T deserialize(String value) {
- return decoder.apply(value);
- }
-
- @Override
- public String serialize(T value) {
- return encoder.apply(value);
- }
-
- @Override
- public String toString(){
- return "Codec(decoder="+decoder.getClass().getName()+", encoder="+encoder.getClass().getName()+")";
- }
- });
- }
-
- /**
- * Get an adapter converting to the given target type.
- * @param targetType the target type class
- * @return true, if the given target type is supported.
- */
- default <T> Codec<T> getAdapter(Class<T> targetType){
- return getCodec(targetType, null);
- }
-
- /**
- * 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 org.apache.tamaya.ConfigException if the target type is not supported.
- */
- <T> Codec<T> getCodec(Class<T> targetType, WithCodec annotation);
-
- /**
- * Checks if the given target type is supported, i.e. a adapter is registered and accessible.
- * @param targetType the target type class
- * @return true, if the given target type is supported.
- */
- boolean isTargetTypeSupported(Class<?> targetType);
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/spi/DefaultServiceComparator.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/DefaultServiceComparator.java b/api/src/main/java/org/apache/tamaya/spi/DefaultServiceComparator.java
deleted file mode 100644
index c53a52d..0000000
--- a/api/src/main/java/org/apache/tamaya/spi/DefaultServiceComparator.java
+++ /dev/null
@@ -1,85 +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.spi;
-
-import java.util.*;
-
-/**
- * Simple comparator based on a Collection of {@link org.apache.tamaya.spi.OrdinalProvider} instances.
- */
-final class DefaultServiceComparator implements Comparator<Object>{
-
- /**
- * List of ordinal providers loaded.
- */
- private List<OrdinalProvider> ordinalProviders = new ArrayList<>();
-
- DefaultServiceComparator(Collection<? extends OrdinalProvider> providers){
- ordinalProviders.addAll(Objects.requireNonNull(providers));
- ordinalProviders.sort(this::compare);
- }
-
- private int compare(OrdinalProvider provider1, OrdinalProvider provider2){
- int o1 = getOrdinal(provider1);
- int o2 = getOrdinal(provider2);
- int order = o1-o2;
- if(order < 0){
- return -1;
- }
- else if(order > 0){
- return 1;
- }
- return 0;
- }
-
- private int getOrdinal(OrdinalProvider provider){
- if(provider instanceof Orderable){
- return ((Orderable)provider).order();
- }
- return 0;
- }
-
- public int getOrdinal(Object service){
- for(OrdinalProvider provider: ordinalProviders){
- OptionalInt ord = provider.getOrdinal(service.getClass());
- if(ord.isPresent()){
- return ord.getAsInt();
- }
- }
- if(service instanceof Orderable){
- return ((Orderable)service).order();
- }
- return 0;
- }
-
-
- @Override
- public int compare(Object o1, Object o2) {
- int ord1 = getOrdinal(o1);
- int ord2 = getOrdinal(o2);
- int order = ord1-ord2;
- if(order < 0){
- return -1;
- }
- else if(order > 0){
- return 1;
- }
- return 0;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/spi/DefaultServiceContextProvider.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/DefaultServiceContextProvider.java b/api/src/main/java/org/apache/tamaya/spi/DefaultServiceContextProvider.java
index 295bab7..e1d1740 100644
--- a/api/src/main/java/org/apache/tamaya/spi/DefaultServiceContextProvider.java
+++ b/api/src/main/java/org/apache/tamaya/spi/DefaultServiceContextProvider.java
@@ -33,12 +33,6 @@ class DefaultServiceContextProvider implements ServiceContext {
private final ConcurrentHashMap<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
/** Singletons. */
private final ConcurrentHashMap<Class, Optional<?>> singletons = new ConcurrentHashMap<>();
- /** Comparator for ordering of multiple services found. */
- private DefaultServiceComparator serviceComparator;
-
- public DefaultServiceContextProvider(){
- serviceComparator = new DefaultServiceComparator(getServices(OrdinalProvider.class, Collections.emptyList()));
- }
@Override
public <T> Optional<T> getService(Class<T> serviceType) {
@@ -94,9 +88,6 @@ class DefaultServiceContextProvider implements ServiceContext {
if(services.isEmpty()){
services.addAll(defaultList);
}
- if(!serviceType.equals(OrdinalProvider.class)) {
- services.sort(serviceComparator);
- }
services = Collections.unmodifiableList(services);
final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>)services);
return previousServices != null ? previousServices : services;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/spi/Orderable.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/Orderable.java b/api/src/main/java/org/apache/tamaya/spi/Orderable.java
deleted file mode 100644
index 56a99d7..0000000
--- a/api/src/main/java/org/apache/tamaya/spi/Orderable.java
+++ /dev/null
@@ -1,34 +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.spi;
-
-/**
- * Interface that can be optionally implemented by SPI components to be loaded into
- * the Tamaya's ServiceContext. The ordinal provided will be used to determine
- * priority and precedence, when multiple components implement the same
- * service interface.
- */
-@FunctionalInterface
-public interface Orderable {
- /**
- * Get the ordinal keys for the component, by default 0.
- * @return the ordinal keys
- */
- int order();
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/spi/OrdinalProvider.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/OrdinalProvider.java b/api/src/main/java/org/apache/tamaya/spi/OrdinalProvider.java
deleted file mode 100644
index aad8618..0000000
--- a/api/src/main/java/org/apache/tamaya/spi/OrdinalProvider.java
+++ /dev/null
@@ -1,37 +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.spi;
-
-import java.util.OptionalInt;
-
-/**
- * The ordinal provider is an optional component that provides an abstraction for ordering/prioritizing
- * services loaded. This can be used to determine, which SPI should be used, if multiple instances are
- * available, or for ordering chain of services.
- * @see ServiceContext
- */
-public interface OrdinalProvider {
- /**
- * Evaluate the ordinal number for the given type.
- * @param type the target type, not null.
- * @return the ordinal, if not defined, 0 should be returned.
- */
- OptionalInt getOrdinal(Class<?> type);
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/main/java/org/apache/tamaya/spi/PropertyAdapterSpi.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/PropertyAdapterSpi.java b/api/src/main/java/org/apache/tamaya/spi/PropertyAdapterSpi.java
new file mode 100644
index 0000000..a1222a4
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/spi/PropertyAdapterSpi.java
@@ -0,0 +1,72 @@
+/*
+ * 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.spi;
+
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.PropertyAdapter;
+import org.apache.tamaya.annotation.WithPropertyAdapter;
+
+
+/**
+ * Manager for {@link org.apache.tamaya.Configuration} instances. Implementations must register an instance
+ * using the {@link org.apache.tamaya.spi.ServiceContextManager} mechanism in place (by default this is based on the {@link java.util.ServiceLoader}.
+ * The {@link org.apache.tamaya.Configuration} Singleton in the API delegates its corresponding calls to the
+ * instance returned by the current bootstrap service in place.
+ *
+ * @see org.apache.tamaya.Configuration
+ * @see org.apache.tamaya.spi.ServiceContextManager
+ */
+public interface PropertyAdapterSpi {
+
+
+ /**
+ * 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.
+ */
+ <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> 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.
+ */
+ default <T> PropertyAdapter<T> getAdapter(Class<T> targetType){
+ return getPropertyAdapter(targetType, null);
+ }
+
+ /**
+ * 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 org.apache.tamaya.ConfigException if the target type is not supported.
+ */
+ <T> PropertyAdapter<T> getPropertyAdapter(Class<T> targetType, WithPropertyAdapter annotation);
+
+ /**
+ * Checks if the given target type is supported, i.e. a adapter is registered and accessible.
+ * @param targetType the target type class
+ * @return true, if the given target type is supported.
+ */
+ boolean isTargetTypeSupported(Class<?> targetType);
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java
----------------------------------------------------------------------
diff --git a/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java b/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java
index 0ec2aa0..858786d 100644
--- a/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java
+++ b/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java
@@ -87,19 +87,4 @@ public class TestConfigServiceSingletonSpi implements ConfigurationSpi {
return expression;
}
- @Override
- public void addChangeListener(Consumer<ConfigChangeSet> l) {
- // ignore
- }
-
- @Override
- public void removeChangeListener(Consumer<ConfigChangeSet> l) {
- // ignore
- }
-
- @Override
- public void publishChange(ConfigChangeSet configChangeSet) {
- // ignore
- }
-
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java
----------------------------------------------------------------------
diff --git a/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java b/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java
index b27164c..65e6c1d 100644
--- a/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java
+++ b/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java
@@ -29,71 +29,71 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.tamaya.annotation.WithCodec;
-import org.apache.tamaya.spi.CodecSpi;
+import org.apache.tamaya.annotation.WithPropertyAdapter;
+import org.apache.tamaya.spi.PropertyAdapterSpi;
/**
- * Test implementation current {@link org.apache.tamaya.spi.CodecSpi}, which provides codecs
+ * Test implementation current {@link org.apache.tamaya.spi.PropertyAdapterSpi}, which provides propertyAdapters
* for some basic types.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
-public final class TestPropertyAdaptersSingletonSpi implements CodecSpi {
+public final class TestPropertyAdaptersSingletonSpi implements PropertyAdapterSpi {
- private Map<Class, Codec<?>> codecs = new ConcurrentHashMap<>();
+ private Map<Class, PropertyAdapter<?>> propertyAdapters = new ConcurrentHashMap<>();
private TestPropertyAdaptersSingletonSpi(){
- register(char.class, (s) -> s.charAt(0), (ch) -> String.valueOf(ch));
- register(int.class, Integer::parseInt, Object::toString);
- register(byte.class, Byte::parseByte, Object::toString);
- register(short.class, Short::parseShort, Object::toString);
- register(boolean.class, Boolean::parseBoolean, b -> String.valueOf(b));
- register(float.class, Float::parseFloat, f -> String.valueOf(f));
- register(double.class, Double::parseDouble, d -> String.valueOf(d));
+ register(char.class, (s) -> s.charAt(0));
+ register(int.class, Integer::parseInt);
+ register(byte.class, Byte::parseByte);
+ register(short.class, Short::parseShort);
+ register(boolean.class, Boolean::parseBoolean);
+ register(float.class, Float::parseFloat);
+ register(double.class, Double::parseDouble);
- register(Character.class, (s) -> s.charAt(0), Object::toString);
- register(Integer.class, Integer::valueOf, Object::toString);
- register(Byte.class, Byte::valueOf, Object::toString);
- register(Short.class, Short::valueOf, String::valueOf);
- register(Boolean.class, Boolean::valueOf, String::valueOf);
- register(Float.class, Float::valueOf, String::valueOf);
- register(Double.class, Double::valueOf, String::valueOf);
- register(BigDecimal.class, BigDecimal::new, String::valueOf);
- register(BigInteger.class, BigInteger::new, String::valueOf);
+ register(Character.class, (s) -> s.charAt(0));
+ register(Integer.class, Integer::valueOf);
+ register(Byte.class, Byte::valueOf);
+ register(Short.class, Short::valueOf);
+ register(Boolean.class, Boolean::valueOf);
+ register(Float.class, Float::valueOf);
+ register(Double.class, Double::valueOf);
+ register(BigDecimal.class, BigDecimal::new);
+ register(BigInteger.class, BigInteger::new);
- register(Currency.class, Currency::getInstance, Object::toString);
+ register(Currency.class, Currency::getInstance);
- register(LocalDate.class, LocalDate::parse, Object::toString);
- register(LocalTime.class, LocalTime::parse, Object::toString);
- register(LocalDateTime.class, LocalDateTime::parse, Object::toString);
- register(ZoneId.class, ZoneId::of, ZoneId::getId);
+ register(LocalDate.class, LocalDate::parse);
+ register(LocalTime.class, LocalTime::parse);
+ register(LocalDateTime.class, LocalDateTime::parse);
+ register(ZoneId.class, ZoneId::of);
}
@Override
- public <T> Codec<T> register(Class<T> targetType, Codec<T> codec){
+ public <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> codec){
Objects.requireNonNull(targetType);
Objects.requireNonNull(codec);
- return (Codec<T>) codecs.put(targetType, codec);
+ return (PropertyAdapter<T>) propertyAdapters.put(targetType, codec);
}
@Override
- public <T> Codec<T> getCodec(Class<T> targetType, WithCodec annotation){
+ public <T> PropertyAdapter<T> getPropertyAdapter(Class<T> targetType, WithPropertyAdapter annotation){
if(annotation!=null){
Class<?> adapterType = annotation.value();
- if(!adapterType.equals(Codec.class)){
+ if(!adapterType.equals(PropertyAdapter.class)){
try{
- return (Codec<T>)adapterType.newInstance();
+ return (PropertyAdapter<T>)adapterType.newInstance();
}
catch(Exception e){
throw new ConfigException("Failed to load PropertyAdapter: " + adapterType, e);
}
}
}
- return (Codec<T>) codecs.get(targetType);
+ return (PropertyAdapter<T>) propertyAdapters.get(targetType);
}
@Override
public boolean isTargetTypeSupported(Class<?> targetType){
- return codecs.containsKey(targetType);
+ return propertyAdapters.containsKey(targetType);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi
----------------------------------------------------------------------
diff --git a/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi b/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi
deleted file mode 100644
index e9b04b4..0000000
--- a/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi
+++ /dev/null
@@ -1,19 +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 current the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-org.apache.tamaya.TestPropertyAdaptersSingletonSpi
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi
----------------------------------------------------------------------
diff --git a/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi b/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi
new file mode 100644
index 0000000..e9b04b4
--- /dev/null
+++ b/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy current the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+org.apache.tamaya.TestPropertyAdaptersSingletonSpi
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java b/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
index 240ccbe..43d6957 100644
--- a/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
+++ b/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
@@ -22,9 +22,7 @@ import org.apache.tamaya.*;
import org.apache.tamaya.core.properties.PropertySourceBuilder;
import java.io.Serializable;
-import java.time.Instant;
import java.util.Map;
-import java.util.Objects;
/**
* Configuration implementation that stores all current values current a given (possibly dynamic, contextual and non remote
@@ -41,7 +39,7 @@ final class FreezedConfiguration extends AbstractConfiguration implements Serial
*/
private FreezedConfiguration(Configuration config){
super(config.getName());
- this.properties = PropertySourceBuilder.of(config).buildFreezed();
+ this.properties = PropertySourceBuilder.of(config).buildFrozen();
}
public static final Configuration of(Configuration config){
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultCodecSpi.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultCodecSpi.java b/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultCodecSpi.java
deleted file mode 100644
index fbbf130..0000000
--- a/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultCodecSpi.java
+++ /dev/null
@@ -1,169 +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.core.internal.config;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.ZoneId;
-import java.util.Currency;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-
-import org.apache.tamaya.Codec;
-import org.apache.tamaya.ConfigException;
-import org.apache.tamaya.annotation.WithCodec;
-import org.apache.tamaya.spi.CodecSpi;
-
-/**
- * Default codecs singleton, which provides default codesc for all kind of classes out of the box, which will be
- * instantiatable from configuration, if one of the following is given:
- * <ul>
- * <li>static factory methods using a String as simgle argument, called {@code of, valueOf, getInstance, instance, parse}</li>
- * <li>have constructors taking a single String</li>
- * </ul>
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public class DefaultCodecSpi implements CodecSpi {
-
-
- private Map<Class,Codec> adapters = new ConcurrentHashMap<>();
-
- public DefaultCodecSpi(){
- // Add default adapters
- register(char.class, (s) -> s.charAt(0), (ch) -> String.valueOf(ch));
- register(byte.class, Byte::parseByte, Object::toString);
- register(short.class, Short::parseShort, Object::toString);
- register(int.class, Integer::parseInt, Object::toString);
- register(long.class, Long::parseLong, Object::toString);
- register(boolean.class, Boolean::parseBoolean, b -> String.valueOf(b));
- register(float.class, Float::parseFloat, f -> String.valueOf(f));
- register(double.class, Double::parseDouble, d -> String.valueOf(d));
-
- register(Character.class, (s) -> s.charAt(0), Object::toString);
- register(Byte.class, Byte::valueOf, Object::toString);
- register(Short.class, Short::valueOf, String::valueOf);
- register(Integer.class, Integer::valueOf, Object::toString);
- register(Long.class, Long::valueOf, Object::toString);
- register(Boolean.class, Boolean::valueOf, b -> String.valueOf(b));
- register(Float.class, Float::valueOf, f -> String.valueOf(f));
- register(Double.class, Double::valueOf, d -> String.valueOf(d));
- register(BigDecimal.class, BigDecimal::new, String::valueOf);
- register(BigInteger.class, BigInteger::new, String::valueOf);
-
- register(Currency.class, Currency::getInstance, Object::toString);
-
- register(LocalDate.class, LocalDate::parse, Object::toString);
- register(LocalTime.class, LocalTime::parse, Object::toString);
- register(LocalDateTime.class, LocalDateTime::parse, Object::toString);
- register(ZoneId.class, ZoneId::of, ZoneId::getId);
- }
-
- @Override
- public <T> Codec<T> register(Class<T> targetType, Codec<T> adapter){
- return adapters.put(targetType, adapter);
- }
-
- @Override
- public <T> Codec<T> getCodec(Class<T> targetType, WithCodec adapterAnnot){
- Codec codec = null;
- Class<? extends Codec> configuredCodec = null;
- if(adapterAnnot != null){
- configuredCodec = adapterAnnot.value();
- if(!configuredCodec.equals(Codec.class)){
- try{
- codec = configuredCodec.newInstance();
- }
- catch(Exception e){
- throw new ConfigException("Invalid codec configured.", e);
- }
- }
- }
- if(codec == null){
- codec = adapters.get(targetType);
- }
- if(codec == null){
- codec = getDefaultCodec(targetType);
- }
- if(codec == null){
- throw new ConfigException("No Codec found for " + targetType.getName());
- }
- return codec;
- }
-
- private <T> Codec getDefaultCodec(Class<T> targetType) {
- Function<String, T> decoder = null;
- Method factoryMethod = getFactoryMethod(targetType, "of", "valueOf", "instanceOf", "getInstance", "from", "parse");
- if(factoryMethod!=null){
- decoder = (s) -> {
- try{
- factoryMethod.setAccessible(true);
- return targetType.cast(factoryMethod.invoke(s));
- }
- catch (Exception e){
- throw new ConfigException("Failed to decode '"+s+"'", e);
- }
- };
- }
- if(decoder==null) {
- try {
- Constructor<T> constr = targetType.getDeclaredConstructor(String.class);
- decoder = (s) -> {
- try{
- constr.setAccessible(true);
- return constr.newInstance(s);
- }
- catch (Exception e){
- throw new ConfigException("Failed to decode '"+s+"'", e);
- }
- };
- } catch (Exception e) {
- // ignore, TODO log finest
- }
- }
- if(decoder!=null) {
- return register(targetType, decoder, String::valueOf);
- }
- return null;
- }
-
- private Method getFactoryMethod(Class<?> type, String... methodNames) {
- Method m;
- for(String name:methodNames){
- try{
- m = type.getDeclaredMethod(name, String.class);
- return m;
- }
- catch(Exception e){
- // ignore, TODO log finest
- }
- }
- return null;
- }
-
- @Override
- public boolean isTargetTypeSupported(Class<?> targetType){
- return adapters.containsKey(targetType);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultPropertyAdapterSpi.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultPropertyAdapterSpi.java b/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultPropertyAdapterSpi.java
new file mode 100644
index 0000000..f1b14e0
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultPropertyAdapterSpi.java
@@ -0,0 +1,168 @@
+/*
+ * 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.core.internal.config;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.Currency;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.PropertyAdapter;
+import org.apache.tamaya.annotation.WithPropertyAdapter;
+import org.apache.tamaya.spi.PropertyAdapterSpi;
+
+/**
+ * Default codecs singleton, which provides default codesc for all kind of classes out of the box, which will be
+ * instantiatable from configuration, if one of the following is given:
+ * <ul>
+ * <li>static factory methods using a String as simgle argument, called {@code of, valueOf, getInstance, instance, parse}</li>
+ * <li>have constructors taking a single String</li>
+ * </ul>
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class DefaultPropertyAdapterSpi implements PropertyAdapterSpi {
+
+
+ private Map<Class,PropertyAdapter> adapters = new ConcurrentHashMap<>();
+
+ public DefaultPropertyAdapterSpi(){
+ // Add default adapters
+ register(char.class, (s) -> s.charAt(0));
+ register(byte.class, Byte::parseByte);
+ register(short.class, Short::parseShort);
+ register(int.class, Integer::parseInt);
+ register(long.class, Long::parseLong);
+ register(boolean.class, Boolean::parseBoolean);
+ register(float.class, Float::parseFloat);
+ register(double.class, Double::parseDouble);
+
+ register(Character.class, (s) -> s.charAt(0));
+ register(Byte.class, Byte::valueOf);
+ register(Short.class, Short::valueOf);
+ register(Integer.class, Integer::valueOf);
+ register(Long.class, Long::valueOf);
+ register(Boolean.class, Boolean::valueOf);
+ register(Float.class, Float::valueOf);
+ register(Double.class, Double::valueOf);
+ register(BigDecimal.class, BigDecimal::new);
+ register(BigInteger.class, BigInteger::new);
+
+ register(Currency.class, Currency::getInstance);
+
+ register(LocalDate.class, LocalDate::parse);
+ register(LocalTime.class, LocalTime::parse);
+ register(LocalDateTime.class, LocalDateTime::parse);
+ register(ZoneId.class, ZoneId::of);
+ }
+
+ @Override
+ public <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> adapter){
+ return adapters.put(targetType, adapter);
+ }
+
+ @Override
+ public <T> PropertyAdapter<T> getPropertyAdapter(Class<T> targetType, WithPropertyAdapter adapterAnnot){
+ PropertyAdapter codec = null;
+ Class<? extends PropertyAdapter> configuredCodec = null;
+ if(adapterAnnot != null){
+ configuredCodec = adapterAnnot.value();
+ if(!configuredCodec.equals(PropertyAdapter.class)){
+ try{
+ codec = configuredCodec.newInstance();
+ }
+ catch(Exception e){
+ throw new ConfigException("Invalid codec configured.", e);
+ }
+ }
+ }
+ if(codec == null){
+ codec = adapters.get(targetType);
+ }
+ if(codec == null){
+ codec = getDefaultPropertyAdapter(targetType);
+ }
+ if(codec == null){
+ throw new ConfigException("No Codec found for " + targetType.getName());
+ }
+ return codec;
+ }
+
+ private <T> PropertyAdapter getDefaultPropertyAdapter(Class<T> targetType) {
+ PropertyAdapter<T> decoder = null;
+ Method factoryMethod = getFactoryMethod(targetType, "of", "valueOf", "instanceOf", "getInstance", "from", "parse");
+ if(factoryMethod!=null){
+ decoder = (s) -> {
+ try{
+ factoryMethod.setAccessible(true);
+ return targetType.cast(factoryMethod.invoke(s));
+ }
+ catch (Exception e){
+ throw new ConfigException("Failed to decode '"+s+"'", e);
+ }
+ };
+ }
+ if(decoder==null) {
+ try {
+ Constructor<T> constr = targetType.getDeclaredConstructor(String.class);
+ decoder = (s) -> {
+ try{
+ constr.setAccessible(true);
+ return constr.newInstance(s);
+ }
+ catch (Exception e){
+ throw new ConfigException("Failed to decode '"+s+"'", e);
+ }
+ };
+ } catch (Exception e) {
+ // ignore, TODO log finest
+ }
+ }
+ if(decoder!=null) {
+ return register(targetType, decoder);
+ }
+ return null;
+ }
+
+ private Method getFactoryMethod(Class<?> type, String... methodNames) {
+ Method m;
+ for(String name:methodNames){
+ try{
+ m = type.getDeclaredMethod(name, String.class);
+ return m;
+ }
+ catch(Exception e){
+ // ignore, TODO log finest
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isTargetTypeSupported(Class<?> targetType){
+ return adapters.containsKey(targetType);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/internal/config/FallbackSimpleConfigProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/FallbackSimpleConfigProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/config/FallbackSimpleConfigProvider.java
index 42140c7..7f997e5 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/config/FallbackSimpleConfigProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/FallbackSimpleConfigProvider.java
@@ -1,7 +1,7 @@
package org.apache.tamaya.core.internal.config;
-import org.apache.tamaya.AggregationPolicy;
import org.apache.tamaya.Configuration;
+import org.apache.tamaya.core.properties.AggregationPolicy;
import org.apache.tamaya.core.properties.PropertySourceBuilder;
import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java b/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java
index 661bf64..8286768 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java
@@ -3,10 +3,8 @@ package org.apache.tamaya.core.internal.config;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.UUID;
import org.apache.tamaya.Configuration;
-import org.apache.tamaya.MetaInfo;
/**
* Implementation of Configuration which the information is from xml or properties files.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java
index 0fd542b..43d2a20 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java
@@ -286,7 +286,7 @@ class AntPathMatcher {
if (tokenized == null) {
tokenized = tokenizePath(pattern);
if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
- // Try to deserialize to the runtime situation that we're encountering:
+ // Try to adapt to the runtime situation that we're encountering:
// There are obviously too many different patterns coming in here...
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
deactivatePatternCache();
@@ -340,7 +340,7 @@ class AntPathMatcher {
if (matcher == null) {
matcher = new AntPathStringMatcher(pattern);
if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
- // Try to deserialize to the runtime situation that we're encountering:
+ // Try to adapt to the runtime situation that we're encountering:
// There are obviously too many different patterns coming in here...
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
deactivatePatternCache();
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/properties/AbstractClasspathAwarePropertySource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/AbstractClasspathAwarePropertySource.java b/core/src/main/java/org/apache/tamaya/core/properties/AbstractClasspathAwarePropertySource.java
index 58fb301..47eb150 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/AbstractClasspathAwarePropertySource.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/AbstractClasspathAwarePropertySource.java
@@ -22,9 +22,6 @@ import org.apache.tamaya.core.resource.Resource;
import org.apache.tamaya.spi.ServiceContext;
import org.apache.tamaya.core.resource.ResourceLoader;
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-
import java.util.*;
public abstract class AbstractClasspathAwarePropertySource extends AbstractPropertySource {
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java b/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
index 6fe3df0..fbfd6df 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertySource.java
@@ -21,7 +21,6 @@ package org.apache.tamaya.core.properties;
import java.io.Serializable;
import java.util.*;
-import org.apache.tamaya.MetaInfo;
import org.apache.tamaya.PropertySource;
/**
@@ -78,7 +77,7 @@ public abstract class AbstractPropertySource implements PropertySource, Serializ
@Override
public String toString(){
StringBuilder b = new StringBuilder(getClass().getSimpleName()).append("{\n");
- b.append(" ").append("(").append(MetaInfo.NAME).append(" = ").append(getName()).append(")\n");
+ b.append(" ").append("(").append(getName()).append(" = ").append(getName()).append(")\n");
printContents(b);
return b.append('}').toString();
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java b/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java
new file mode 100644
index 0000000..99be931
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java
@@ -0,0 +1,133 @@
+/*
+* 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.core.properties;
+
+import org.apache.tamaya.ConfigException;
+
+import java.util.logging.Logger;
+
+/**
+* Policy that defines how the different configurations/property sources should be aggregated.
+* This is done by a mapping function defined as follows:
+* <pre>
+* function f(key, value1, value2) -> result
+*
+* whereas
+*
+* key = the fully qualified property key,
+* value1 = the value from the first configuration/property source (can be null)
+* value2 = the value from the second configuration/property source (can be null)
+*
+* result = the value to be used in the aggregation, or null, which removed the
+* key from the result.
+* </pre>
+*
+* Of course, during this evaluation step additional actions can be taken, e.g. refer to #LOG_ERROR, which
+* ignores duplicate entries, but also logs the conflict on severe/error level.
+*/
+public interface AggregationPolicy {
+
+ /**
+ * Method which decides how keys/values are aggregated.
+ * @param key the key current the entry, must not be {@code null}.
+ * @param currentValue the current keys, or {@code null}.
+ * @param newValue the new keys, never {@code null}.
+ * @return the target keys to be used in the resulting property set, or null, to remove the property.
+ */
+ public String aggregate(String key, String currentValue, String newValue);
+
+ /** Ignore overrides, only extend (additive). */
+ public static final AggregationPolicy IGNORE_DUPLICATES = (k, v1, v2) -> v1 == null? v2 : v1;
+
+ /** Combine multiple values into a comma separated list. */
+ public static final AggregationPolicy COMBINE = (k, v1, v2) -> v1 != null && v2 != null ? v1 + ',' + v2: v2;
+
+ /**
+ * Interpret later keys as override (additive and override), replacing
+ * the key loaded earlier/fromMap previous contained
+ * {@link org.apache.tamaya.PropertySource}.
+ */
+ public static final AggregationPolicy OVERRIDE = (k, v1, v2) -> v2;
+
+ /**
+ * Throw an exception, when keys are not disjunctive (strictly
+ * additive).
+ */
+ public static final AggregationPolicy EXCEPTION =
+ (String key, String value, String newValue) -> {
+ if(value!=null && newValue!=null && !value.equals(newValue)){
+ throw new ConfigException("Conflicting values encountered key="+key+", keys="+value+", newValue="+newValue);
+ }
+ return newValue;
+ };
+
+ /**
+ * Ignores any duplicates, but logs the conflict encountered to error/severe level.
+ */
+ public static final AggregationPolicy LOG_ERROR =
+ (String key, String value, String newValue) -> {
+ if(value!=null && newValue!=null && !value.equals(newValue)){
+ Logger.getLogger(AggregationPolicy.class.getName())
+ .severe(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
+ return value;
+ }
+ return newValue;
+ };
+
+ /**
+ * Ignores any duplicates, but logs the conflict encountered to info level.
+ */
+ public static final AggregationPolicy LOG_WARNING =
+ (String key, String value, String newValue) -> {
+ if(value!=null && newValue!=null && !value.equals(newValue)){
+ Logger.getLogger(AggregationPolicy.class.getName())
+ .warning(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
+ return value;
+ }
+ return newValue;
+ };
+
+ /**
+ * Ignores any duplicates, but logs the conflict encountered to info level.
+ */
+ public static final AggregationPolicy LOG_INFO =
+ (String key, String value, String newValue) -> {
+ if(value!=null && newValue!=null && !value.equals(newValue)){
+ Logger.getLogger(AggregationPolicy.class.getName())
+ .info(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
+ return value;
+ }
+ return newValue;
+ };
+
+ /**
+ * Ignores any duplicates, but logs the conflict encountered to debug/finest level.
+ */
+ public static final AggregationPolicy LOG_DEBUG =
+ (String key, String value, String newValue) -> {
+ if(value!=null && newValue!=null && !value.equals(newValue)){
+ Logger.getLogger(AggregationPolicy.class.getName())
+ .finest(() -> "Conflicting values encountered key=" + key + ", keys=" + value + ", newValue=" + newValue);
+ return value;
+ }
+ return newValue;
+ };
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/properties/BuildablePropertySource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/BuildablePropertySource.java b/core/src/main/java/org/apache/tamaya/core/properties/BuildablePropertySource.java
index 33eb2a6..847fbe9 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/BuildablePropertySource.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/BuildablePropertySource.java
@@ -17,13 +17,11 @@
*/
package org.apache.tamaya.core.properties;
-import org.apache.tamaya.MetaInfo;
import org.apache.tamaya.PropertySource;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.UUID;
/**
* Created by Anatole on 07.12.2014.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertySource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertySource.java b/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertySource.java
index 9958f8d..d0b2172 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertySource.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertySource.java
@@ -18,9 +18,6 @@
*/
package org.apache.tamaya.core.properties;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.core.properties.AbstractPropertySource;
-
import java.util.Map;
class EnvironmentPropertySource extends AbstractPropertySource {
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/properties/URLBasedPropertySource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/URLBasedPropertySource.java b/core/src/main/java/org/apache/tamaya/core/properties/URLBasedPropertySource.java
index 8086c23..1d79537 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/URLBasedPropertySource.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/URLBasedPropertySource.java
@@ -18,7 +18,6 @@
*/
package org.apache.tamaya.core.properties;
-import org.apache.tamaya.AggregationPolicy;
import org.apache.tamaya.ConfigException;
import org.apache.tamaya.core.internal.resources.io.UrlResource;
import org.apache.tamaya.core.resource.Resource;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/spi/CodecProviderSpi.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/CodecProviderSpi.java b/core/src/main/java/org/apache/tamaya/core/spi/CodecProviderSpi.java
deleted file mode 100644
index b814037..0000000
--- a/core/src/main/java/org/apache/tamaya/core/spi/CodecProviderSpi.java
+++ /dev/null
@@ -1,36 +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.core.spi;
-
-import org.apache.tamaya.Codec;
-
-/**
- * This service provides different {@link org.apache.tamaya.Codec} instances for types.
- */
-public interface CodecProviderSpi {
-
- /**
- * Called, when a given {@link org.apache.tamaya.Configuration} has to be evaluated.
- *
- * @return the corresponding {@link java.util.function.Function<String, T>}, or {@code null}, if
- * not available for the given target type.
- */
- <T> Codec<T> getCodec(Class<T> type);
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceComparator.java b/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceComparator.java
new file mode 100644
index 0000000..2fb719c
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceComparator.java
@@ -0,0 +1,85 @@
+/*
+ * 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.core.spi;
+
+import java.util.*;
+
+/**
+ * Simple comparator based on a Collection of {@link OrdinalProvider} instances.
+ */
+final class DefaultServiceComparator implements Comparator<Object>{
+
+ /**
+ * List of ordinal providers loaded.
+ */
+ private List<OrdinalProvider> ordinalProviders = new ArrayList<>();
+
+ DefaultServiceComparator(Collection<? extends OrdinalProvider> providers){
+ ordinalProviders.addAll(Objects.requireNonNull(providers));
+ ordinalProviders.sort(this::compare);
+ }
+
+ private int compare(OrdinalProvider provider1, OrdinalProvider provider2){
+ int o1 = getOrdinal(provider1);
+ int o2 = getOrdinal(provider2);
+ int order = o1-o2;
+ if(order < 0){
+ return -1;
+ }
+ else if(order > 0){
+ return 1;
+ }
+ return 0;
+ }
+
+ private int getOrdinal(OrdinalProvider provider){
+ if(provider instanceof Orderable){
+ return ((Orderable)provider).order();
+ }
+ return 0;
+ }
+
+ public int getOrdinal(Object service){
+ for(OrdinalProvider provider: ordinalProviders){
+ OptionalInt ord = provider.getOrdinal(service.getClass());
+ if(ord.isPresent()){
+ return ord.getAsInt();
+ }
+ }
+ if(service instanceof Orderable){
+ return ((Orderable)service).order();
+ }
+ return 0;
+ }
+
+
+ @Override
+ public int compare(Object o1, Object o2) {
+ int ord1 = getOrdinal(o1);
+ int ord2 = getOrdinal(o2);
+ int order = ord1-ord2;
+ if(order < 0){
+ return -1;
+ }
+ else if(order > 0){
+ return 1;
+ }
+ return 0;
+ }
+}
[3/4] incubator-tamaya git commit: TAMAYA-19: Streamlined API and
impl.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceContextProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceContextProvider.java b/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceContextProvider.java
new file mode 100644
index 0000000..9a2fb1b
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/spi/DefaultServiceContextProvider.java
@@ -0,0 +1,112 @@
+/*
+ * 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.core.spi;
+
+import org.apache.tamaya.spi.ServiceContext;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class implements the (default) {@link org.apache.tamaya.spi.ServiceContext} interface and hereby uses the JDK
+ * {@link java.util.ServiceLoader} to load the services required.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+class DefaultServiceContextProvider implements ServiceContext {
+ /** List current services loaded, per class. */
+ private final ConcurrentHashMap<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
+ /** Singletons. */
+ private final ConcurrentHashMap<Class, Optional<?>> singletons = new ConcurrentHashMap<>();
+ /** Comparator for ordering of multiple services found. */
+ private DefaultServiceComparator serviceComparator;
+
+ public DefaultServiceContextProvider(){
+ serviceComparator = new DefaultServiceComparator(getServices(OrdinalProvider.class, Collections.emptyList()));
+ }
+
+ @Override
+ public <T> Optional<T> getService(Class<T> serviceType) {
+ Optional<T> cached = (Optional<T>)singletons.get(serviceType);
+ if(cached==null) {
+ List<? extends T> services = getServices(serviceType, Collections.emptyList());
+ if (services.isEmpty()) {
+ cached = Optional.empty();
+ }
+ else{
+ cached = Optional.of(services.get(0));
+ }
+ singletons.put(serviceType, cached);
+ }
+ return cached;
+ }
+
+ /**
+ * Loads and registers services.
+ *
+ * @param serviceType
+ * The service type.
+ * @param <T>
+ * the concrete type.
+ * @param defaultList
+ * the list current items returned, if no services were found.
+ * @return the items found, never {@code null}.
+ */
+ @Override
+ public <T> List<? extends T> getServices(final Class<T> serviceType, final List<? extends T> defaultList) {
+ List<T> found = (List<T>) servicesLoaded.get(serviceType);
+ if (found != null) {
+ return found;
+ }
+ return loadServices(serviceType, defaultList);
+ }
+
+ /**
+ * Loads and registers services.
+ *
+ * @param serviceType The service type.
+ * @param <T> the concrete type.
+ * @param defaultList the list current items returned, if no services were found.
+ *
+ * @return the items found, never {@code null}.
+ */
+ private <T> List<? extends T> loadServices(final Class<T> serviceType, final List<? extends T> defaultList) {
+ try {
+ List<T> services = new ArrayList<>();
+ for (T t : ServiceLoader.load(serviceType)) {
+ services.add(t);
+ }
+ if(services.isEmpty()){
+ services.addAll(defaultList);
+ }
+ if(!serviceType.equals(OrdinalProvider.class)) {
+ services.sort(serviceComparator);
+ }
+ services = Collections.unmodifiableList(services);
+ final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>)services);
+ return previousServices != null ? previousServices : services;
+ } catch (Exception e) {
+ Logger.getLogger(DefaultServiceContextProvider.class.getName()).log(Level.WARNING,
+ "Error loading services current type " + serviceType, e);
+ return defaultList;
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/spi/Orderable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/Orderable.java b/core/src/main/java/org/apache/tamaya/core/spi/Orderable.java
new file mode 100644
index 0000000..5397776
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/spi/Orderable.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.core.spi;
+
+/**
+ * Interface that can be optionally implemented by SPI components to be loaded into
+ * the Tamaya's ServiceContext. The ordinal provided will be used to determine
+ * priority and precedence, when multiple components implement the same
+ * service interface.
+ */
+@FunctionalInterface
+public interface Orderable {
+ /**
+ * Get the ordinal keys for the component, by default 0.
+ * @return the ordinal keys
+ */
+ int order();
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/spi/OrdinalProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/OrdinalProvider.java b/core/src/main/java/org/apache/tamaya/core/spi/OrdinalProvider.java
new file mode 100644
index 0000000..2d7f057
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/spi/OrdinalProvider.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.core.spi;
+
+import java.util.OptionalInt;
+
+/**
+ * The ordinal provider is an optional component that provides an abstraction for ordering/prioritizing
+ * services loaded. This can be used to determine, which SPI should be used, if multiple instances are
+ * available, or for ordering chain of services.
+ * @see org.apache.tamaya.spi.ServiceContext
+ */
+public interface OrdinalProvider {
+ /**
+ * Evaluate the ordinal number for the given type.
+ * @param type the target type, not null.
+ * @return the ordinal, if not defined, 0 should be returned.
+ */
+ OptionalInt getOrdinal(Class<?> type);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/java/org/apache/tamaya/core/spi/PropertyAdapterProviderSpi.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/PropertyAdapterProviderSpi.java b/core/src/main/java/org/apache/tamaya/core/spi/PropertyAdapterProviderSpi.java
new file mode 100644
index 0000000..b8e7122
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/spi/PropertyAdapterProviderSpi.java
@@ -0,0 +1,36 @@
+/*
+ * 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.core.spi;
+
+import org.apache.tamaya.PropertyAdapter;
+
+/**
+ * This service provides different {@link org.apache.tamaya.PropertyAdapter} instances for types.
+ */
+public interface PropertyAdapterProviderSpi {
+
+ /**
+ * Called, when a given {@link org.apache.tamaya.Configuration} has to be evaluated.
+ *
+ * @return the corresponding {@link java.util.function.Function<String, T>}, or {@code null}, if
+ * not available for the given target type.
+ */
+ <T> PropertyAdapter<T> getPropertyAdapter(Class<T> type);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi
----------------------------------------------------------------------
diff --git a/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi b/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi
deleted file mode 100644
index c0a61aa..0000000
--- a/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.CodecSpi
+++ /dev/null
@@ -1,19 +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 current the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-org.apache.tamaya.core.internal.config.DefaultCodecSpi
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi
----------------------------------------------------------------------
diff --git a/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi b/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi
new file mode 100644
index 0000000..0554453
--- /dev/null
+++ b/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdapterSpi
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy current the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+org.apache.tamaya.core.internal.config.DefaultPropertyAdapterSpi
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties.java b/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties.java
index 7571c1e..50e1753 100644
--- a/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties.java
+++ b/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties.java
@@ -26,11 +26,10 @@ import static org.junit.Assert.assertNotNull;
import java.util.Map;
import java.util.stream.Collectors;
-import org.apache.tamaya.AggregationPolicy;
import org.apache.tamaya.Configuration;
-import org.apache.tamaya.MetaInfo;
import org.apache.tamaya.PropertySource;
import org.apache.tamaya.core.config.ConfigFunctions;
+import org.apache.tamaya.core.properties.AggregationPolicy;
import org.apache.tamaya.core.properties.PropertySourceBuilder;
import org.junit.Test;
@@ -88,7 +87,6 @@ public class UC1ReadProperties {
assertNotNull(config);
assertTrue(config.isEmpty());
assertTrue(config.getProperties().isEmpty());
- assertFalse(config.isMutable());
}
@Test
@@ -97,7 +95,6 @@ public class UC1ReadProperties {
assertNotNull(provider);
assertTrue(provider.isEmpty());
assertTrue(provider.getProperties().isEmpty());
- assertFalse(provider.isMutable());
}
@Test
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/core/src/test/java/org/apache/tamaya/ucs/UC2CombineProperties.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/ucs/UC2CombineProperties.java b/core/src/test/java/org/apache/tamaya/ucs/UC2CombineProperties.java
index 3a6e471..60ab6ea 100644
--- a/core/src/test/java/org/apache/tamaya/ucs/UC2CombineProperties.java
+++ b/core/src/test/java/org/apache/tamaya/ucs/UC2CombineProperties.java
@@ -18,10 +18,9 @@
*/
package org.apache.tamaya.ucs;
-import org.apache.tamaya.AggregationPolicy;
import org.apache.tamaya.ConfigException;
-import org.apache.tamaya.MetaInfo;
import org.apache.tamaya.PropertySource;
+import org.apache.tamaya.core.properties.AggregationPolicy;
import org.apache.tamaya.core.properties.PropertySourceBuilder;
import org.junit.Test;
@@ -34,7 +33,7 @@ import org.junit.Test;
public class UC2CombineProperties {
/**
- * The most common use cases is aggregating two property config to new provider, hereby {@link org.apache.tamaya.AggregationPolicy}
+ * The most common use cases is aggregating two property config to new provider, hereby {@link org.apache.tamaya.core.properties.AggregationPolicy}
* defines the current variants supported.
*/
@Test
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/design/2_API.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/design/2_API.adoc b/docs/src/main/asciidoc/design/2_API.adoc
new file mode 100644
index 0000000..69a06c1
--- /dev/null
+++ b/docs/src/main/asciidoc/design/2_API.adoc
@@ -0,0 +1,469 @@
+// 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.
+<<<
+[[API]]
+== The Tamaya API
+=== Overview
+Though Tamaya is a very powerful and flexible solution there are basically only a few simple core concepts required that build
+the base of all the other mechanisms:
+
+The API provides these artifacts, which are:
+
+* A simple but complete SE API for accessing key/value based _Configuration_.
+* _Configuration_ hereby models configuration and as well provides the static entry point to access configuration.
+ _ Configuration_ provides
+ ** access to literal key/value pairs.
+ ** +PropertyAdapter+ support to convert String values to arbitrary non-String types for type safe configuration access.
+ ** functional extension points (+with,query+) based un +UnaryOperator<Configuration>+ (operator) and +Function<Configuration,T>+ (query).
+ ** provides static access to the current +Configuration+ (default configuration)
+ ** provides static access to the additional named +Configuration+ instances
+ ** a service to inject configuration into beans, including listener and callback support
+ ** a service for creating onfiguration _templates_ based on interfaces.
+* +PropertyAdapter+ defines a functional interface for converting String values into any required target types. It also
+ provides static access to the adapters registered to implement transparent type conversion as needed, if possible.
+* _PropertySource:_ is the the SPI for a source that provides configuration data. A +PropertySource+
+ hereby
+ ** is designed as a minimalistic data interface to be implemented by any kind of data providers (local or remote)
+ ** provides data key/value pairs in raw format as String key/values only
+ ** can optionally support scanning of its provided values
+
+* _Annotations_ a set of annotations allows to configure configuration injection on classes or interface (aka config templates).
+
+The SPI contains the following core concepts/artifacts:
+
+* _ServiceContext_ is the delegate singleton that is used by the framework to resolve components. The effective component
+ loading can be accessed by implementing and registering an instance of +ServiceContextProvider+ using +java.util.ServiceLoader+.
+* All the singleton used explicitly (+Configuration, PropertyAdapter+) are backed up corresponding API interfaces
+ (+ConfigurationSpi, PropertyAdapterSpi+).
+ To override a singleton's behaviour the corresponding SPI has to be implemented and registered, so it can be loaded
+ by the current +ServiceContext+ setup (by default ServiceLoader based).
+
+This is also reflected in the main parts of the API, which is quite small:
+
+* +org.apache.tamaya+ contains the main abstractions +Configuration, ConfigQuery, PropertyAdapter,
+ PropertySource+ and +ConfigException+
+* +org.apache.tamaya.spi+ contains the SPI interfaces to be implemented by implementations and the +ServiceContextManager+
+ mechanism (+ConfigurationSpi, PropertyAdapterSpi, ServiceContext+).
++ +org.apache.tamaya.annot+ contains the annotations defined to control configuration injection.
+
+So basically an implementation has to implement the SPIs provided. The +ServiceContext+ only has to be overridden, when
+a default SE +java.util.ServiceLoader+ mechanism is not sufficient.
+
+[[APIKeyValues]]
+=== Key/Value Pairs
+
+Basically configuration is a very generic concept. Therefore it should be modelled in a generic way. The most simple
+and most commonly used approach are simple literal key/value pairs. So the core building block of {name} are key/value pairs.
+You can think of a common +.properties+ file, e.g.
+
+[source,properties]
+.A simple properties file
+--------------------------------------------
+a.b.c=cVal
+a.b.c.1=cVal1
+a.b.c.2=cVal2
+a=aVal
+a.b=abVal
+a.b2=abVal
+--------------------------------------------
+
+Now you can use +java.util.Properties+ to read this file and access the corresponding properties, e.g.
+
+[source,properties]
+.Accessing some properties
+--------------------------------------------
+Properties props = new Properties();
+props.readProperties(...);
+String val = props.getProperty("a.b.c");
+val = props.getProperty("a.b.c.1");
+...
+--------------------------------------------
+
+This looks familiar to most of you. Nevertheless when looking closer to the above key/value pairs,
+there are more things in place: looking at the keys +a.b.c+, +a.b.c.1+, +a.b.c.2+, +a+, +a.b+ we
+see that the key names build up a flattened tree structure. So we can define the following:
+
+Given a key +p1.p2.p3.k=value+:
+
+* +p1.p2.p3.k+ is called the _qualified key_
+* +p1.p2.p3+ is the key's _area_
+* the child areas +p1.p2", "p1+ are called _areas_ as well
+* +k+ is the _(unqualified) key_
+
+This terminology is used also later ta some locations. Nevertheless given that you can perform some very useful actions:
+
+* you can filter the keys with an area. E.g. in the example before you can query for all keys within the area +a.b.c+
+ and map them to new property set.
+* you can access all child keys of an area
+* you can evaluate the areas present.
+* ...and more.
+
+All this kind of actions (and more) must not be included in the API, because they can be modelled as +ConfigQuery+ instances and
+implemented/provided by implementation code.
+
+
+==== Why Using Strings Only
+
+There are good reason to keep of non String-values as core storage representation of configuration. Mostly
+there are several huge advantages:
+
+* Strings are simple to understand
+* Strings are human readable and therefore easy to prove for correctness
+* Strings can easily be used within different language, different VMs, files or network communications.
+* Strings can easily be compared and manipulated
+* Strings can easily be searched, indexed and cached
+* It is very easy to provide Strings as configuration, which gives much flexibility for providing configuration in
+ production as well in testing.
+* and more...
+
+On the other side there are also disadvantages:
+
+* Strings are inherently not type safe, they do not provide validation out of the box for special types, such as
+numbers, dates etc.
+* In many cases you want to access configuration in a typesafe way avoiding conversion to the target types explicitly
+ throughout your code.
+* Strings are neither hierarchical nor multi-valued, so mapping hierarchical and collection structures requires some
+ extra efforts.
+
+Nevertheless most of these advantages can be mitigated easily, hereby still keeping all the benefits from above:
+
+* Adding type safe adapters on top of String allow to add any type easily, that can be directly mapped out of Strings.
+ This includes all common base types such as numbers, dates, time, but also timezones, formatting patterns and more.
+* Also multi-valued, complex and collection types can be defined as a corresponding +PropertyAdapter+ knows how to
+ parse and create the target instance required.
+* String s also can be used as references pointing to other locations and formats, where configuration is
+ accessible.
+
+
+[API PropertySource]
+=== PropertySource
+==== Basic Model
+
+We have seen that constraining configuration aspects to simple literal key/value pairs provides us with an easy to
+understand, generic, flexible, yet expendable mechanism. Looking at the Java language features a +java.util.Map<String,
+String>+ and +java.util.Properties+ basically model these aspects out of the box.
+
+Though there are advantages in using these types as a model, there are some severe drawbacks, mostly implementation
+of these types is far not trivial or the basic model has sever drawbacks, because of backward compatibility with
+the original collection API.
+
+To make implementation of a custom property source as convinient as possible only the following methods were
+identified to be necessary:
+
+[source,java]
+.Interface PropertySource
+--------------------------------------------
+public interface PropertySource{
+
+ Optional<String> get(String key);
+ boolean isBrowseable();
+ Map<String, String> getProperties();
+
+}
+--------------------------------------------
+
+Hereby
+
+* +get+ looks similar to the methods on +Map+, though +get+ uses the +Optional+ type introduced
+ with Java 8. This avoids returning +null+ or throwing exceptions in case no such entry is available and also
+ reduces the API's footprint, since default values can be easily implemented by calling +Optional.orElse+ and
+ similar methods.
+* +getProperties+ allows to extract mapped data to a +Map+. Other methods like +containsKey, keySet+ as well as
+ streaming operations then can be applied on the returned +Map+ instance.
+* But not in all scenarios a property source may be browseable. This can be evaluated by calling +isBrowseable()+.
+
+This interface can be implemented by any kind of logic. It could be a simple in memory map, a distributed configuration
+provided by a data grid, a database, the JNDI tree or other resources. Or it can be a combination of multiple
+property sources with additional combination/aggregation rules in place.
+
+==== Meta Information
+
+Meta information is not explicitly modelled, since it can be easily added by some key naming schemes. E.g. look at
+the example below, which return a map of all metadata keys for +a.b.c+.:
+
+[source,java]
+.Modelling Meta Data
+--------------------------------------------
+PropertySource src = ...;
+Map<String, String> metaData = src.getArea("a.b.c[meta]");
+--------------------------------------------
+
+The API does not provide any explicit support for meta-data, whereas implementations may provide metadata editors
+or readers.
+
+==== Mutability
+
+In general Property sources can be modeled as mutable. Nevertheless the API does not support out of the box mutability,
+due to the following reasons:
+
+* Mutability is rather complex
+* Mutability is only rarely required
+* Mutability can be implemented in various ways
+
+As a consequence mutability mechanisms may be provided by implementations as needed, but are not part of the API.
+
+
+[[API Configuration]]
+=== Configuration
+==== Basic Model: Extending PropertySource
+
++Configuration+ inherits all basic features from +PropertySource+, but additionally adds functionality for
+type safety and external features of any interacting with configuration:
+
+[source,java]
+.Interface Configuration
+--------------------------------------------
+public interface Configuration extends PropertySource{
+ // type support
+ default Optional<Boolean> getBoolean(String key);
+ default OptionalInt getInteger(String key);
+ default OptionalLong getLong(String key);
+ default OptionalDouble getDouble(String key);
+ default <T> Optional<T> getAdapted(String key, PropertyAdapter<T> adapter);
+ <T> Optional<T> get(String key, Class<T> type);
+
+ // extension points
+ default Configuration with(UnaryOperator<Configuration> operator);
+ default <T> T query(ConfigQuery<T> query);
+}
+--------------------------------------------
+
+Hereby
+
+* +XXX getXXX(String)+ provide type safe accessors for all basic wrapper types of the JDK. Basically all this
+ methods delegate to the +getAdapted+ method, additionally passing the required +PropertyAdapter+.
+* +getAdapted+ allow accessing any type, hereby also passing a +PropertyAdapter+ that converts
+ the configured literal value to the type required.
+* +with, query+ provide the extension points for adding additional functionality.
+
+Additionally +Configuration+ provides several access methods:
+
+[source,java]
+.Interface Configuration
+--------------------------------------------
+public interface Configuration extends PropertySource{
+ ...
+
+ // accessors for configuration
+ public static Configuration current();
+ public static Configuration current(String name);
+ public static boolean isAvailable(String name);
+ // accessors for template and injection
+ public static <T> T createTemplate(Class<T> template, Configuration... configurations);
+ public static void configure(Object instance, Configuration... configurations);
+}
+--------------------------------------------
+
+Hereby
+* +current()+ returns the _default_ +Configuration+
+* +current(String name)+ returns a named +Configuration+ (there may be arbitrary additional +Configuration+ instance
+ additionally to the default +Configuration+ instance.
+* +isAvailable(String name)+ allows to determine if a named +Configuration+ is available.
+* +createTemplate(Class<T> template, Configuration... configurations)+ allows to create a new template instance based
+ on a (optionally) annotated interface. The according getter methods are backed up and implemented by Tamaya based
+ on the configuration values available. The +configurations+ parameter allows parts of +Configuration+ instances to be
+ passed that override any instances available through +current(name), current()+.
+* +configure+ performs injection of configured values on a (optionally) annotated non abstract type.
+ The +configurations+ parameter allows parts of +Configuration+ instances to be
+ passed that override any instances available through +current(name), current()+.
+
+
+[[TypeConversion]]
+==== Type Conversion
+
+Configuration extend +PropertySource+ and adds additional support for non String types. This is achieved
+with the help of +PropertyAdapter+ instances:
+
+[source,java]
+.PropertyAdapter
+--------------------------------------------
+@FunctionalInterface
+public interface PropertyAdapter<T>{
+ T adapt(String value);
+}
+--------------------------------------------
+
++PropertyAdapter+ instances can be implemented manually or registered and accessed from the
++PropertyAdaper+ using static methods. Hereby the exact mechanism is determined by the implementation
+of +PropertyAdapterSpi+ backing up the static methods.
+By default corresponding +PropertyAdapter+ instances can be registered using the Java +ServiceLoader+
+mechanism, or programmatically ba calling the +register(Class, PropertyAdapter)+.
+
+[source,java]
+.PropertyAdapter
+--------------------------------------------
+@FunctionalInterface
+public interface PropertyAdapter<T>{
+ T adapt(String value);
+
+ public static <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> adapter);
+ public static boolean isTargetTypeSupported(Class<?> targetType);
+ public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType);
+ public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType, WithPropertyAdapter annotation);
+}
+--------------------------------------------
+
+The now a typed instance of a +Configuration+ is required, by default the +Configuration+ implementation acquires
+a matching +PropertyAdapter+. If one is found it can easily pass the String value from its String property store
+for converting it to the required target type. In the normal case for the mostly needed types this is completely
+transparent to the user.
+But basically this mechanism can also be used for adaptive filtering of values accessed. As an example lets assume
+we want to decode an encryped password on the fly, so we can achieve this with as less code as follows:
+
+[source,java]
+.Simple Filtering Adapter Use Case
+--------------------------------------------
+Configuration config = Configuration.cuirrent();
+String decryptedPassword = config.getAdapted(String.class, "user.password", p -> PKI.decrypt(p));
+--------------------------------------------
+
+[[Injection]]
+=== Inversion of Control
+==== Overview
+
+Inversion of Control (aka IoC/the Hollywood Principle) has proven to be very handy and effective in avoiding boilerplate
+code. In Java there are different frameworks available that all provide IoC mechanisms. Unfortunately IoC is not a
+built-in language feature. So for a portable solution OOTB that works also in Java SE Tamaya itself has to provide the
+according injection services. As an example refer to the following example:
+
+[source,java]
+.Annotated Example Class
+--------------------------------------------
+public class ConfiguredClass{
+
+ // resolved by default, using property name, class and package name
+ private String testProperty;
+
+ @ConfiguredProperty(config="pluginConfig", keys={"a.b.c.key1","a.b.legacyKey"})
+ @ConfiguredProperty(config="productConfig", keys="area1.key2")
+ @DefaultValue("The current \\${JAVA_HOME} env property is ${env:JAVA_HOME}.")
+ String value1;
+
+ @ConfiguredProperty(keys="a.b.c.key2")
+ private int value2;
+
+ // resolved by default
+ @DefaultValue("http://127.0.0.1:8080/res/api/v1/info.json")
+ private URL accessUrl;
+
+ // Config injection disabled for this property
+ @NoConfig
+ private Integer int1;
+
+ @ConfiguredProperty("BD")
+ @WithAdapter(MyBigDecimalRoundingAdapter.class)
+ private BigDecimal bigNumber;
+
+ ...
+}
+--------------------------------------------
+
+The class does not show all (but most all) the possibilities that are provided. Configuring an instance of the
+class using Tamaya is very simple:
+
+[source,java]
+.Configuring the +ConfiguredClass+ Instance
+--------------------------------------------
+ConfiguredClass classInstance = new ConfiguredClass();
+Configuration.configure(configuredClass);
+--------------------------------------------
+
+==== The Annotations in detail
+
+tbd
+
+The +Configuration+ interface provides static methods that allow to any kind of instances be configured
+ny just passing the instances calling +Configuration.configure(instance);+. The classes passed hereby must
+be annotated with +@ConfiguredProperty+ to define the configured properties. Hereby this annotation can be
+used in multiple ways and combined with other annotations such as +@DefaultValue+,
++@WithLoadPolicy+, +@WithConfig+, +@WithConfigOperator+, +@WithPropertyAdapter+.
+
+To illustrate the mechanism below the most simple variant of a configured class is given:
+
+[source,java]
+.Most simple configured class
+--------------------------------------------
+pubic class ConfiguredItem{
+ @ConfiguredProperty
+ private String aValue;
+}
+--------------------------------------------
+
+When this class is configured, e.g. by passing it to +Configuration.configure(Object)+,
+the following is happening:
+
+* The current valid +Configuration+ is evaluated by calling +Configuration cfg = Configuration.of();+
+* The current property value (String) is evaluated by calling +cfg.get("aValue");+
+* if not successful, an error is thrown (+ConfigException+)
+* On success, since no type conversion is involved, the value is injected.
+* The configured bean is registered as a weak change listener in the config system's underlying
+ configuration, so future config changes can be propagated (controllable by applying the
+ +@WithLoadPolicy+ annotation).
+
+In the next example we explicitly define the property value:
+[source,java]
+--------------------------------------------
+pubic class ConfiguredItem{
+
+ @ConfiguredProperty
+ @ConfiguredProperty("a.b.value")
+ @configuredProperty("a.b.deprecated.value")
+ @DefaultValue("${env:java.version}")
+ private String aValue;
+}
+--------------------------------------------
+
+Within this example we evaluate multiple possible keys. Evaluation is aborted if a key could be successfully
+resolved. Hereby the ordering of the annotations define the ordering of resolution, so in the example above
+resolution equals to +"aValue", "a.b.value", "a.b.deprecated.value"+. If no value could be read
+from the configuration, it uses the value from the +@DefaultValue+ annotation. Interesting here
+is that this value is not static, it is evaluated by calling +Configuration.evaluateValue(Configuration, String)+.
+
+
+=== Extension Points
+
+We are well aware of the fact that this library will not be able to cover all kinds of use cases. Therefore
+we have added functional extension mechanisms to +Configuration+ that were used in other areas of the Java eco-system as well:
+
+* +with(UnaryOperator<Configuration> operator)+ allows to pass arbitrary functions that take adn return instances of +Configuration+.
+ They can be used to cover use cases such as filtering, configuration views, security interception and more.
+* +query(Function<Configuration,T> query)+ ConfigQuery+ defines a function returning any kind of result based on a
+ configuration instance. Queries are used for accessing/deriving any kind of data of a +Configuration+ instance,
+ e.g. accessing a +Set<String>+ of area keys present.
+
+Both interfaces hereby are functional interfaces, defined in +java.util.function+ and can be applied using Lambdas or
+method references:
+
+[source,java]
+.Applying a Configuration Query
+--------------------------------------------
+ConfigSecurity securityContext = Configuration.current().query(ConfigSecurity::targetSecurityContext);
+--------------------------------------------
+
+NOTE: +ConfigSecurity+ is an arbitrary class.
+
+Or an operator calls looks as follows:
+
+[source,java]
+.Applying a Configuration Operators
+--------------------------------------------
+Configuration secured = Configuration.current().with(ConfigSecurity::secure);
+--------------------------------------------
+
+
+== SPI
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a60570e8/docs/src/main/asciidoc/design/2_CoreConcepts.adoc
----------------------------------------------------------------------
diff --git a/docs/src/main/asciidoc/design/2_CoreConcepts.adoc b/docs/src/main/asciidoc/design/2_CoreConcepts.adoc
deleted file mode 100644
index 831a71f..0000000
--- a/docs/src/main/asciidoc/design/2_CoreConcepts.adoc
+++ /dev/null
@@ -1,849 +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.
-<<<
-[[CoreConcepts]]
-== {name} Core Concepts
-Though {name} is a very powerful and flexible solution there are basically only a few simple core concepts required that build
-the base of all the other mechanisms:
-
-The API contains the following core concepts/artifacts:
-
-* Literal Key/Value Pairs
-* _PropertySource:_ is the the SPI for a source that provides configuration data. A +PropertySource+
- hereby defines
- ** a minimalistic SPI to be implemented by the config data source
- ** provides data key/value pairs in raw format as String key/values only
- ** providers should not have any dependencies other than to the datasource
- ** providers may read context dependent data, but basically providers themselves are not contextual.
- Context management should be done by the ConfigurationProvider implementation that also is responsible
- for combining a set of property providers to a Configuration.
- _Configuration_ is the API that users of Tamaya will see, when they access configuration in raw format. Hereby +Configuration+
- ** adds type support for non String types
- ** provides functional extension points (+with,query+)
- ** allows registering/deregistering of change listeners
- ** is the entry point for evaluating the current +Configuration+
- ** each +PropertySource+ can be easily converted into a +Configuration+
- ** allows configuration entries to be injected
- ** to access configuration _templates_ (annotated interfaces).
- ** Configuration may support mutability by allowing instances of +ConfigChangeSet+ to be passed.
-* _PropertySourceBuilder_ allows to aggregate different property sources. Hereby property sources are
- seen as sets, which can be combined to new sources using set styled operations (aggregation, intersection, subtraction).
- This allows to model and create composite sources, to build up more complex configuration models
- step by step.
-* _MetaInfo_ is provided by each +Configuration, PropertySource+ and describes the configuration/provider and its entries.
-* _Environment_ is the base model for modelling the environment and the accessor for getting the current +Environment+ instance.
-* _Annotations_ a set of annotations allows to configure configuration injection on classes or interface (aka config templates).
-
-The SPI contains the following core concepts/artifacts:
-
-* _ServiceContext_ is the delegate singleton that is used by the framework to resolve components. The effective component
- loading can be accessed by implementing and registering an instance of +ServiceContextProvider+ using +java.util.ServiceLoader+.
-* All the singleton used explicitly (+Environment,Configuration+ are backed up corresponding API interfaces.
- To override a singleton's behaviour the corresponding SPI has to be implemented and registered, so it can be loaded
- by the current +ServiceContext+ setup (by default ServiceLoader based).
-* Also the singleton used implicitly by +Configuration, Environment+ are backed up corresponding SPI interfaces.
- To override a singleton's behaviour the corresponding SPI has to be implemented and registered, so it can be loaded
- by the current +ServiceContext+ setup (by default ServiceLoader based).
-
-This is also reflected in the main parts of the API, which is quite small:
-
-* +org.apache.tamaya+ contains the main abstractions +Configuration, ConfigQuery, PropertyAdapter,
- Environment, PropertySource, MetaInfo+
-* +org.apache.tamaya.spi+ contains the SPI interfaces to be implemented by implementations and the +ServiceContext+ mechanism.
-+ +org.apache.tamaya.annot+ contains the annotations defined.
-
-In the implementation are there are additional projects:
-
-* +org.apache.tamaya.core+ contains the core implementation of the API. Deploying it together with the API results in a
- flexible framework that can be easily used for configuration of different complexity. But out of the box this framework
- will not do much more than exposing system and environment properties, its power comes when an additional meta-model
- is defined and deployed. Hereby you can write your own, or use on e of the provided ones (see later).
-* the core part is extended by multiple additional modules
- ** CDI integration
- ** Default configuration meta-models and providers for the most common usage scenarios
- *** standalone applications
- *** Java EE
- *** ...
-
-These parts are explained in the following sections. It is recommended that user's of the API read through this part.
-All subsequent parts are building upon this concepts and may be more difficult to understand without having read
-this section.
-
-
-[[APIKeyValues]]
-=== Key/Value Pairs
-
-Basically configuration is a very generic concept. Therefore it should be modelled in a generic way. The most simple
-and similarly most commonly used are simple literal key/value pairs. So the core building block of {name} are key/value pairs.
-You can think of a common +.properties+ file, e.g.
-
-[source,properties]
-.A simple properties file
---------------------------------------------
-a.b.c=cVal
-a.b.c.1=cVal1
-a.b.c.2=cVal2
-a=aVal
-a.b=abVal
-a.b2=abVal
---------------------------------------------
-
-Now you can use +java.util.Properties+ to read this file and access the corresponding properties, e.g.
-
-[source,properties]
-.Accessing some properties
---------------------------------------------
-Properties props = new Properties();
-props.readProperties(...);
-String val = props.getProperty("a.b.c");
-val = props.getProperty("a.b.c.1");
-...
---------------------------------------------
-
-This looks familiar to most of you. Nevertheless when looking closer to the above key/value pairs,
-there are more concepts in place: looking at the keys +a.b.c+, +a.b.c.1+, +a.b.c.2+, +a+, +a.b+ we
-see that the key names build up a flattened tree structure. So we can define the following:
-
-Given a key +p1.p2.p3.k=value+:
-
-* +p1.p2.p3.k+ is called the _qualified key_
-* +p1.p2.p3+ is the key's _area_
-* the child areas +p1.p2", "p1+ are called _areas_ as well
-* +k+ is the _(unqualified) key_
-
-Given that you can perform some very useful actions:
-
-* you can filter the keys with an area. E.g. in the example before you can query for all keys within the area +a.b.c+
- and map them to new properties set as follows:
-
-[source,properties]
-.Accessing an area
---------------------------------------------
-1=cVal1
-2=cVal2
---------------------------------------------
-
-Similarly accessing the area +a+ results in the following properties:
-
-[source,properties]
-.Accessing the area +a+
---------------------------------------------
-b=abVal
-b2=abVal
---------------------------------------------
-
-Additionally you can access all values of an area recursively, so accessing +a+ recursively results in
-the following properties:
-
-[source,properties]
-.Accessing area +a+ recursively
---------------------------------------------
-b.c=cVal
-b.c.1=cVal1
-b.c.2=cVal2
-b=abVal
-b2=abVal
---------------------------------------------
-
-Why this is useful? Well there are different use cases:
-
-* you can segregate your configuration properties, e.g. a module can access its module configuration by
- querying all properties under the area +config.modules.myModule+ (or whatever would be appropriate).
-* you can use this mechanism to configure maps (or more generally: collections).
-* you can easily filter parts of configuration
-* ...and more.
-
-==== Why Using Strings Only
-
-Using Strings as base representation of configuration comes with several huge advantages:
-
-* Strings are simple to understand
-* Strings are human readable and therefore easy to prove for correctness
-* Strings can easily be used within different language, different VMs, files or network communications.
-* Strings can easily be compared and manipulated
-* Strings can easily be searched, indexed and cached
-* It is very easy to provide Strings as configuration, which gives much flexibility for providing configuration in
- production as well in testing.
-* and more
-
-On the other side there are also disadvantages:
-
-* Strings are inherently not type safe, they do not provide validation out of the box for special types, such as
-numbers,
- dates etc.
-* Often you want not to work with Strings, but with according types.
-* Strings are not hierarchical, so mapping hierarchical structures requires some extra efforts.
-
-Nevertheless most of these advantages can be mitigated easily, hereby still keeping all the benefits from above:
-
-* Adding type safe converters on top of String allow to add any type easily, that can be directly mapped out of Strings.
- This includes all common base types such as numbers, dates, time, but also timezones, formatting patterns and more.
-* Even more complex mappings can be easily realized, by using String not as a direct representation of configuration,
- but a reference that defines where the more complex configuration artifact is available. This mechanism is similarly
- easy to understand as parsing Strings to numbers, but is powerful enough to provide e.g. all kind of deployment
- descriptors in Java EE.
-* Hierarchical and collection types can be mapped in different ways:
-** The keys of configuration can have additional syntax/semantics. E.g. when adding dor-separating path semantics
-*** trees/maps can also simply be mapped.
-
-[API PropertySource]
-=== PropertySource
-==== Basic Model
-
-We have seen that constrain configuration aspects to simple literal key/value pairs provides us with an easy to
-understand, generic, flexible, yet expendable mechanism. Looking at the Java language features a +java.util.Map<String,
-String>+ and +java.util.Properties+ basically model these quite well out of the box.
-So it would make sense to build configuration on top of the JDK's +Map+ interface. This creates immediately additional
-benefits:
-
-* we inherit full Lambda and collection support
-* Maps are widely known and well understood
-
-Nevertheless there are some severe drawbacks:
-
-* +Configuration+ also requires meta-data, such as
-** the origin of a certain configuration entry and how it was derived from other values
-** the sensitivity of some data
-** the provider that have read the data
-** the time, when the data was read
-** the timestamp, when some data may be outdated
-** ...
-
-Basically the same is also the not related to some single configuration key, but also to a whole map.
-The +PropertySource+ interface models exact these aspects and looks as illustrated below:
-
-[source,java]
-.Interface PropertySource
---------------------------------------------
-public interface PropertySource{
-
- Optional<String> get(String key);
- boolean containsKey(String key);
- Map<String, String> toMap();
- MetaInfo getMetaInfo();
-
- default Set<String> keySet();
- default ConfigChangeSet load();
- default boolean isMutable();
- default void apply(ConfigChangeSet change);
-}
---------------------------------------------
-
-Hereby
-
-* +getMetaInfo()+ return the meta information for the property provider, as well as for individual property key/value pairs.
-* +get+ look similar to the methods on +Map+, though +get+ uses the +Optional+ type introduced
- with Java 8. This avoids returning +null+ or throwing exceptions in case no such entry is available and also
- reduced the API's footprint, since default values can be easily implemented by calling +Optional.orElse+.
-* +containsKey, keySet+ are as well methods similar to +java.util.Map+ though implementations may only returns
- limited data, especially when the underlying map storage does not support iteration.
-* +isMutable()+ allows to easy check, if a property provider is mutable, which is more elegant than catching
- +NonSupportedOperation+ exception thrown on the according methods of +Map+.
-* +load()+ finally allows to (re)load a property map. It depends on the implementing source, if this operation
- has any effect. If the map changes an according +ConfigChange+ must be returned, describing the
- changes applied.
-* +toMap+ allows to extract thing to a +Map+. Similar to +containsKey, keySet+ implementations may only return
- a limited data map, especially when the underlying map storage does not support iteration.
-
-This simple model will be used within the spi, where configuration can be injected/provided from external resources.
-But we have seen, that we have to consider additional aspects, such as extendability and type safety. Therefore we
-extend +PropertySource+ and hereby also apply the 'composite pattern', which results in the following key abstraction.
-
-==== Meta Information
-
-Each instance also provides an instance of +MetaInfo+, which provides meta information for the providers and its properties:
-
-[source,java]
-.Accessing Meta Information
---------------------------------------------
-PropertySource prov = ...;
-MetaInfo metaInfo = prov.getMetaInfo();
-Set<String> keys = metaInfo.keySet(); // returns the attribute keys, for which meta-information is accessible.
-String metaData = metaInfo.get("a.b.c.value"); // access meta information
-String itemName = metaInfo.getName(); // access meta information for the provider
---------------------------------------------
-
-As we have seen above there is as well a +MetaInfoBuilder+, which must be used to create instances of
-+MetaInfo+.
-
-==== Mutability
-
-Property sources optionally may be mutable. This can be checked by calling +boolean isMutable()+. If a source
-is mutable a +ConfigChangeSet+ can be passed. This change set can then be applied by the source. On creation
-of the +ConfigChangeSetBuilder+ a source can pass version information, so _optimistic locking_ can be implemented
-easily:
-
-[source,java]
-.Creating and applying a +ConfigChangeSet+ to a PropertySource
---------------------------------------------
-PropertySource source = ...;
-ConfigChangeSet changeSet = ConfigChangeSetBuilder.of(provider) // creating a default version
- .remove("key1ToBeRemoved", +key2ToBeRemoved")
- .put("key2", "key2Value")
- .put("key3", 12345)
- .put("key4", 123.45)
- .build();
-source.apply(changeSet);
---------------------------------------------
-
-
-[[API Configuration]]
-=== Configuration
-==== Basic Model
-
-Configuration inherits all basic features from +PropertySource+, but additionally adds functionality for
-type safety and extension mechanisms:
-
-[source,java]
-.Interface Configuration
---------------------------------------------
-public interface Configuration extends PropertySource{
-
- default Optional<Boolean> getBoolean(String key);
- default OptionalInt getInteger(String key);
- default OptionalLong getLong(String key);
- default OptionalDouble getDouble(String key);
- default <T> Optional<T> getAdapted(String key, PropertyAdapter<T> adapter);
- <T> Optional<T> get(String key, Class<T> type);
-
- // accessing areas
- default Set<String> getAreas();
- default Set<String> getTransitiveAreas();
- default Set<String> getAreas(final Predicate<String> predicate);
- default Set<String> getTransitiveAreas(Predicate<String> predicate);
- default boolean containsArea(String key);
-
- // extension points
- default Configuration with(UnaryOperator<Configuration> operator);
- default <T> T query(ConfigQuery<T> query);
-
- // versioning
- default String getVersion(){return "N/A";}
-
- // singleton accessors
- public static boolean isDefined(String name);
- public static <T> T current(String name, Class<T> template);
- public static Configuration current(String name);
- public static Configuration current();
- public static <T> T current(Class<T> type){
- public static void configure(Object instance);
- public static String evaluateValue(String expression);
- public static String evaluateValue(Configuration config, String expression);
- public static void addChangeListener(ConfigChangeListener listener);
- public static void removeChangeListener(ConfigChangeListener listener);
-}
---------------------------------------------
-
-Hereby
-
-* +XXX getXXX(String)+ provide type safe accessors for all basic wrapper types of the JDK.
-* +getAdapted+ allow accessing any type, hereby also passing a +PropertyAdapter+ that converts
- the configured literal value to the type required.
-* +getAreas()+, +getTransitiveAreas()+ allow to examine the hierarchical tree modeled by the configuration tree.
- Optionally also predicates can be passed to select only part of the tree to be returned.
-* +containsArea+ allows to check, if an area is defined.
-* +with, query+ provide the extension points for adding additional functionality.
-
-* the static accessor methods define:
- ** +current(), current(Class), current(String), current(String, Class)+ return the configuration valid for the current runtime environment.
- ** +addChangeListener, removeChangeListener+ allow to register or unregister
- global config change listener instances.
- ** evaluateValue allows to evaluate a configuration expression based on a given configuration.
- ** +configure+ performs injection of configured values.
-
-[[TypeConversion]]
-==== Type Conversion
-
-Configuration extend +PropertySource+ and add additional support for non String types. This is achieved
-with the help of +PropertyAdapter+ instances:
-
-[source,java]
-.PropertyAdapter
---------------------------------------------
-@FunctionalInterface
-public interface PropertyAdapter<T>{
- T adapt(String value);
-}
---------------------------------------------
-
-PropertyAdapter instances can be implemented manually or registered and accessed from the
-+PropertyAdapers+ singleton. Hereby the exact mechanism is determined by the API backing up the singleton.
-By default corresponding +PropertyAdapter+ instances can be registered using the Java +ServiceLoader+
-mechanism, or programmatically ba calling the +register(Class, PropertyAdapter)+ method.
-
-[source,java]
---------------------------------------------
-public final class PropertyAdapters{
- public static <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> adapter);
- public static boolean isTargetTypeSupported(Class<?> targetType);
- public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType);
- public static <T> PropertyAdapter<T> getAdapter(Class<T> targetType, WithPropertyAdapter annotation);
-}
---------------------------------------------
-
-Whereas this mechanism per se looks not very useful it's power shows up when combining it with the annotations
-API provided, e.g. look at the following annotated class:
-
-[source,java]
-.Annotated Example Class
---------------------------------------------
-public class ConfiguredClass{
-
- @ConfiguredProperty
- private String testProperty;
-
- @ConfiguredProperty("a.b.c.key1")
- @DefaultValue("The current \\${JAVA_HOME} env property is ${env:JAVA_HOME}.")
- String value1;
-
- @ConfiguredProperty("a.b.c.key2")
- private int value2;
-
- @ConfiguredProperty
- @DefaultValue("http://127.0.0.1:8080/res/api/v1/info.json")
- private URL accessUrl;
-
- @ConfiguredProperty
- @DefaultValue("5")
- private Integer int1;
-
- @ConfiguredProperty("a.b.customType")
- private MyCustomType myCustomType;
-
- @ConfiguredProperty("BD")
- private BigDecimal bigNumber;
-
- ...
-}
---------------------------------------------
-
-The class does not show all the possibilities that are provided, but it shows that arbitrary types can be supported easily.
-This applied similarly to collection types, whereas collections are more advanced and therefore described in a separate section
-later.
-
-Given the class above and the current configuration can provide the values required, configuring an instance of the
-class is simple:
-
-[source,java]
-.Configuring the Example Class
---------------------------------------------
-ConfiguredClass classInstance = new ConfiguredClass();
-Configuration.configure(configuredClass);
---------------------------------------------
-
-Additional types can transparently be supported by implementing and registering corresponding SPI instances. This is explained
-in the SPI documentation of {name}.
-
-==== Extension Points
-
-We are well aware of the fact that this library will not be able to cover all kinds of use cases. Therefore
-we have added similar functional extension mechanisms that were used in other areas of the Java eco-system as well:
-
-* +ConfigOperator+ define unary operations on +Configuration+. They can be used for filtering, implementing
- configuration views, security interception etc.
-* +ConfigQuery+ defines a function returning any kind of result based on a configuration instance. Typical
- use cases of queries could be the implementation of configuration SPI instances that are required
- by other libraries or frameworks.
-
-Both interfaces hereby are defined as functional interfaces:
-
-[source,java]
-.ConfigQuery
---------------------------------------------
-@FunctionalInterface
-public interface ConfigQuery<T>{
- T query(Configuration config);
-}
---------------------------------------------
-
-Instances of this interface can be applied on a +Configuration+ instance:
-
-[source,java]
-.Applying Config operators and queries
---------------------------------------------
-ConfigSecurity securityContext = Configuration.current().query(ConfigSecurity::targetSecurityContext);
---------------------------------------------
-
-NOTE: +ConfigSecurity+ is an arbitrary class.
-
-Similarly an instance of +UnaryOpertor<Configuration>+ can be applied as well to decorate an existing +Configuration+
-instance:
-
-[source,java]
-.Applying Config operators
---------------------------------------------
-Configuration secured = Configuration.current().with(ConfigSecurity::secure);
---------------------------------------------
-
-=== Configuration Injection
-
-The +Configuration+ interface provides static methods that allow to anykind of instances be configured
-ny just passing the instances calling +Configuration.configure(instance);+. The classes passed hereby must
-be annotated with +@ConfiguredProperty+ to define the configured properties. Hereby this annotation can be
-used in multiple ways and combined with other annotations such as +@DefaultValue+,
-+@WithLoadPolicy+, +@WithConfig+, +@WithConfigOperator+, +@WithPropertyAdapter+.
-
-To illustrate the mechanism below the most simple variant of a configured class is given:
-
-[source,java]
-.Most simple configured class
---------------------------------------------
-pubic class ConfiguredItem{
- @ConfiguredProperty
- private String aValue;
-}
---------------------------------------------
-
-When this class is configured, e.g. by passing it to +Configuration.configure(Object)+,
-the following is happening:
-
-* The current valid +Configuration+ is evaluated by calling +Configuration cfg = Configuration.of();+
-* The current property value (String) is evaluated by calling +cfg.get("aValue");+
-* if not successful, an error is thrown (+ConfigException+)
-* On success, since no type conversion is involved, the value is injected.
-* The configured bean is registered as a weak change listener in the config system's underlying
- configuration, so future config changes can be propagated (controllable by applying the
- +@WithLoadPolicy+ annotation).
-
-In the next example we explicitly define the property value:
-[source,java]
---------------------------------------------
-pubic class ConfiguredItem{
-
- @ConfiguredProperty
- @ConfiguredProperty("a.b.value")
- @configuredProperty("a.b.deprecated.value")
- @DefaultValue("${env:java.version}")
- private String aValue;
-}
---------------------------------------------
-
-Within this example we evaluate multiple possible keys. Evaluation is aborted if a key could be successfully
-resolved. Hereby the ordering of the annotations define the ordering of resolution, so in the example above
-resolution equals to +"aValue", "a.b.value", "a.b.deprecated.value"+. If no value could be read
-from the configuration, it uses the value from the +@DefaultValue+ annotation. Interesting here
-is that this value is not static, it is evaluated by calling +Configuration.evaluateValue(Configuration, String)+.
-
-[[API ConfigurationBuilder]]
-==== Building Simple Configuration
-
-Looking at the structures of configuration system used by large companies we typically encounter some kind of configuration
-hierarchies that are combined in arbitrary ways. Users of the systems are typically not aware of the complexities in this
-area, since they simply know the possible locations, formats and the overriding policies. Framework providers on the other
-side must face the complexities and it would be very useful if Tamaya can support here by providing prebuilt functionality
-that helps implementing these aspects. All this leads to the feature set of combining property sources. Hereby the following
-strategies are useful:
-
-* aggregating configurations, hereby later configurations added
- ** override any existing entries from earlier configurations
- ** combine conflicting entries from earlier configurations, e.g. into a comma-separated structure.
- ** may throw a ConfigException ig entries are conflicting
- ** may only add entries not yet defined by former providers, preventing entries that are already present to be overwrite
- ** any custom aggregation strategy, which may be a mix of above
-* intersecting configurations
-* subtracting configurations
-* filtering configurations
-
-These common functionality is provided by +ConfigurationBuilder+ instances. Additionally to the base strategies above a
-+MetaInfo+ instance can be passed optionally as well to define the meta information for the newly created configuration.
-Let's assume we have two configurations with the following data:
-
-[source,properties]
-.Configuration 1
---------------------------------------------
-a=a
-b=b
-c=c
-g=g
-h=h
-i=i
---------------------------------------------
-
-[source,properties]
-.Configuration 2
---------------------------------------------
-a=A
-b=B
-c=C
-d=D
-e=E
-f=F
---------------------------------------------
-
-Looking in detail you see that the entries +a,b,c+ are present in both configurations, whereas +d,e,f+ are only present in Configuration 1,
-and +g,h,i+ only in Configuration 2.
-
-[source,java]
-.Example Combining Configurations
---------------------------------------------
-Configuration cfg1 = ...
-Configuration cfg2 = ...
-
-// aggregate, hereby values from Configuration 2 override values from Configuration 1
-Configuration unionOverriding = ConfigurationBuilder.of().aggregate(cfg1, cfg2).build();
-System.out.println("unionOverriding: " + unionOverriding);
-
-// ignore duplicates, values present in Configuration 1 are not overriden by Configuration 2
-Configuration unionIgnoringDuplicates = ConfigurationBuilder.of()
- .withAggregationPolicy(AggregationPolicy.IGNORE_DUPLICATES).aggregate(cfg1, cfg2).build();
-System.out.println("unionIgnoringDuplicates: " + unionIgnoringDuplicates);
-
-// this variant combines/maps duplicate values into a new value
-Configuration unionCombined = ConfigurationBuilder.of().withAggregationPolicy(AggregationPolicy.COMBINE)
- .aggregate(cfg1, cfg2);
-System.out.println("unionCombined: " + unionCombined);
-
-// This variant throws an exception since there are key/value paris in both providers, but with different values
-try{
- ConfigurationBuilder.of().withAggregationPolicy(AggregationPolicy.EXCEPTION).aggregate(provider1, provider2)
- .build();
-}
-catch(ConfigException e){
- // expected!
-}
---------------------------------------------
-
-The example above produces the following outpout:
-
-[source,listing]
-.Example Combining Configurations
---------------------------------------------
-AggregatedConfiguration{
- (name = dynamicAggregationTests)
- a = "[a][A]"
- b = "[b][B]"
- c = "[c][C]"
- d = "[D]"
- e = "[E]"
- f = "[F]"
- g = "[g]"
- h = "[h]"
- i = "[i]"
-}
-unionOverriding: AggregatedConfigurations{
- (name = <noname>)
- a = "A"
- b = "B"
- c = "C"
- d = "D"
- e = "E"
- f = "F"
- g = "g"
- h = "h"
- i = "i"
-}
-unionIgnoringDuplicates: AggregatedConfigurations{
- (name = <noname>)
- a = "a"
- b = "b"
- c = "c"
- d = "D"
- e = "E"
- f = "F"
- g = "g"
- h = "h"
- i = "i"
-}
-unionCombined: AggregatedConfigurations{
- (name = <noname>)
- a = "a,A"
- b = "b,B"
- c = "c,C"
- d = "D"
- e = "E"
- f = "F"
- g = "g"
- h = "h"
- i = "i"
-}
---------------------------------------------
-
-No +AggregationPolicy+ is also a functional interface that can be implemented:
-
-[source,java]
-.AggregationPolicy Interface
---------------------------------------------
-@FunctionalInterface
-public interface AggregationPolicy {
- String aggregate(String key, String value1, String value2);
-}
---------------------------------------------
-
-So we can also define our own aggregation strategy using a Lambda expression:
-
-[source,java]
-.Use a Custom AggregationPolicy
---------------------------------------------
-Configuration cfg1 = ...;
-Configuration cfg2 = ...;
-Configuration config = ConfigurationBuilder.of("dynamicAggregationTests")
- .withAggregationPolicy((k, v1, v2) -> (v1 != null ? v1 : "") + '[' + v2 + "]")
- .aggregate(cfg1, cfg2).build();
-System.out.println(config);
---------------------------------------------
-
-Additionally we also create here an instance of +MetaInfo+. The output of this code snippet is as follows:
-
-[source,listing]
-.Listing of dynamic aggregation policy
---------------------------------------------
-AggregatedConfiguration{
- (name = dynamicAggregationTests)
- a = "[a][A]"
- b = "[b][B]"
- c = "[c][C]"
- d = "[D]"
- e = "[E]"
- f = "[F]"
- g = "[g]"
- h = "[h]"
- i = "[i]"
-}
---------------------------------------------
-
-Summarizing the +ConfigurationBuilder+ allows to combine providers in various forms:
-
-[source,listing]
-.Methods provided on PropertySources
---------------------------------------------
-public final class ConfigurationBuilder {
-
- private ConfigurationBuilder() {}
-
- public static ConfigurationBuilder of();
- public static ConfigurationBuilder of(PropertySource config);
- public static ConfigurationBuilder of(MetaInfo metaInfo);
- public static ConfigurationBuilder of(String name);
-
- public ConfigurationBuilder withMetaInfo(MetaInfo metaInfo);
- public ConfigurationBuilder withAggregationPolicy(AggregationPolicy aggregationPolicy);
-
- public ConfigurationBuilder addArgs(String... args);
- public ConfigurationBuilder addPaths(List<String> paths);
- public ConfigurationBuilder addUrls(URL... urls);
- public ConfigurationBuilder addUrls(List<URL> urls);
- public ConfigurationBuilder addMap(Map<String, String> map);
-
- public ConfigurationBuilder empty();
- public ConfigurationBuilder empty(MetaInfo metaInfo);
- public ConfigurationBuilder emptyMutable(MetaInfo metaInfo);
- public ConfigurationBuilder addEnvironmentProperties();
- public ConfigurationBuilder addSystemProperties();
- public ConfigurationBuilder aggregate(Configuration... configs){
- public ConfigurationBuilder aggregate(List<Configuration> configs) {
- public ConfigurationBuilder mutable(Configuration config) {
- public ConfigurationBuilder intersected(Configuration... providers) {
- public ConfigurationBuilder subtracted(Configuration target, Configuration... providers) {
- public ConfigurationBuilder filtered(Predicate<String> filter, Configuration config) {
- public ConfigurationBuilder contextual(Supplier<Configuration> mapSupplier,
- Supplier<String> isolationKeySupplier) {
- public ConfigurationBuilder delegating(Configuration mainMap, Map<String, String> parentMap) {
- public ConfigurationBuilder replacing(Configuration mainMap, Map<String, String> replacementMap);
-
- public Configuration build();
- public Configuration buildFrozen();
-}
---------------------------------------------
-
-
-
-=== Environment
-
-The environment basically is also a kind of property/value provider similar to +System
-.getenv()+ in the JDK. Nevertheless it provides additional functionality:
-
-[source,java]
-.Interface Environment
---------------------------------------------
-public interface Environment {
-
- Optional<String> get(String key);
- boolean containsKey(String key);
- Set<String> keySet();
- Map<String,String> toMap();
-
- public static Environment current();
- public static Environment root();
---------------------------------------------
-
-* Basically an environment can contain any properties. The root environment
- hereby must contain at least
- ** all JDK's environment properties.
- ** additional root properties are allowed as well.
-* the root environment is always directly accessible by calling +Environment.root()+
-* the current environment can be accessed by calling +Environment.current()+.
-
-Summarizing the Environment can be seen as a runtime context. This also implies, that this context changes
-depending on the current runtime context. Developers implementing an environment mechanism should be aware that
-an environment can be accessed very frequently, so evaluation and access of an +Environment+ must be fast. For
-further details we recommend the SPI details section of the core implementation.
-
-
-== SPI
-
-[[API PropertySourceBuilder]]
-==== Building Property Sources
-
-In [[PropertSource]] we have outlines that the essence of a property key store for configuration can be modelled by
-the +PropertySource+ interface. Similarly to the +ConfigurationBuilder+ you can also combine several +PropertySource+
-instances to assemble more complex configuration scenarios. Typically assembling is done within a +ConfigProvider+,
-which is responsible for providing correct Configuration corresponding to the current environment.
-
-Summarizing you can
-* aggregate providers, hereby later providers added
- ** override any existing entries from earlier providers
- ** combine conflicting entries from earlier providers, e.g. into a comma-separated structure.
- ** may throw a ConfigException ig entries are conflicting
- ** may only add entries not yet defined by former providers, preventing entries that are already present to be overwritten
- ** any custom aggregation strategy, which may be a mix of above
-* intersecting providers
-* subtracting providers
-* filtering providers
-
-The following code snippet gives a couple of examples:
-
-[source,java]
-.Example Combining PropertySources
---------------------------------------------
-PropertySource provider1 = ...
-PropertySource provider2 = ...
-
-// aggregate, hereby values from provider 2 override values from provider 1
-PropertySource unionOverriding = PropertySourceBuilder.of()
- .aggregate(provider1, provider2).build(); // OVERRIDE policy is default
-System.out.println("unionOverriding: " + unionOverriding);
-
-// ignore duplicates, values present in provider 1 are not overriden by provider 2
-PropertySource unionIgnoringDuplicates = PropertySources
- .aggregate(AggregationPolicy.IGNORE_DUPLICATES(), provider1, provider2).build();
-System.out.println("unionIgnoringDuplicates: " + unionIgnoringDuplicates);
-
-// this variant combines/maps duplicate values into a new value
-PropertySource unionCombined = PropertySourceBuilder.of().withAggregationPolicy(AggregationPolicy.COMBINE))
- .aggregate(provider1, provider2).build();
-System.out.println("unionCombined: " + unionCombined);
-
-// This variant throws an exception since there are key/value paris in both providers, but with different values
-try{
- PropertySourceBuilder.of().withAggregationPolicy(AggregationPolicy.EXCEPTION).aggregate(provider1, provider2);
-}
-catch(ConfigException e){
- // expected!
-}
---------------------------------------------
-
-