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[] + +